@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,8 +1,8 @@
|
|
|
1
1
|
import { opendir } from 'node:fs/promises';
|
|
2
2
|
import { basename, resolve } from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
5
|
-
import {
|
|
4
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
5
|
+
import { streamBoundedJsonLines, } from '../readers.js';
|
|
6
6
|
export const CODEX_ADAPTER_VERSION = '1.0.0';
|
|
7
7
|
export const CODEX_SOURCE_SCHEMA_VERSION = '1.0.0';
|
|
8
8
|
const AppServerEnvelopeSchema = z.object({
|
|
@@ -57,7 +57,7 @@ export class CodexSessionAdapter {
|
|
|
57
57
|
for (const root of [...scope.allowedRoots].sort()) {
|
|
58
58
|
for await (const locator of discoverJsonl(resolve(root), this.discoveryLimits.maxDepth)) {
|
|
59
59
|
if (++emitted > this.discoveryLimits.maxFiles) {
|
|
60
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Codex source discovery exceeded the authorized file limit');
|
|
60
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Codex source discovery exceeded the authorized file limit', { limit: 'maxFiles', configured: this.discoveryLimits.maxFiles, observed: emitted });
|
|
61
61
|
}
|
|
62
62
|
yield {
|
|
63
63
|
provider: 'codex',
|
|
@@ -70,30 +70,34 @@ export class CodexSessionAdapter {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
async inspect(source) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
73
|
+
const records = this.readSource(source);
|
|
74
|
+
let result = await records.next();
|
|
75
|
+
while (!result.done)
|
|
76
|
+
result = await records.next();
|
|
77
|
+
return result.value;
|
|
79
78
|
}
|
|
80
79
|
async *stream(source, cursor) {
|
|
80
|
+
yield* this.readSource(source, cursor);
|
|
81
|
+
}
|
|
82
|
+
async *readSource(source, cursor) {
|
|
81
83
|
const start = parseCursor(cursor?.value);
|
|
82
84
|
const input = await streamBoundedJsonLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
|
|
83
85
|
let mode = isAppServerLocator(source.locator)
|
|
84
86
|
? 'app-server' : null;
|
|
85
87
|
let schemaVersion = null;
|
|
86
88
|
const sourceArtifactSessionId = rolloutIdFromFilename(source.locator);
|
|
87
|
-
|
|
89
|
+
const rolloutState = { nativeSessionId: sourceArtifactSessionId };
|
|
88
90
|
const identities = new Map();
|
|
89
91
|
let outputIndex = 0;
|
|
90
92
|
let sawRecord = false;
|
|
93
|
+
let complete = false;
|
|
94
|
+
let sawActive = false;
|
|
91
95
|
for await (const line of input) {
|
|
92
96
|
sawRecord = true;
|
|
93
97
|
mode ??= isAppServerRecord(line.value) ? 'app-server' : 'rollout';
|
|
94
98
|
const currentSchema = declaredSchemaVersion([line]);
|
|
95
99
|
if (schemaVersion && schemaVersion !== currentSchema) {
|
|
96
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'Codex source declares mixed schema versions');
|
|
100
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Codex source declares mixed schema versions', schemaIssueDetail('schemaVersion'));
|
|
97
101
|
}
|
|
98
102
|
schemaVersion = currentSchema;
|
|
99
103
|
assertSupportedSchemaMajor(schemaVersion);
|
|
@@ -101,30 +105,22 @@ export class CodexSessionAdapter {
|
|
|
101
105
|
if (mode === 'app-server') {
|
|
102
106
|
const parsed = AppServerEnvelopeSchema.safeParse(line.value);
|
|
103
107
|
if (!parsed.success) {
|
|
104
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex App Server record is malformed');
|
|
108
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex App Server record is malformed', schemaRejectionDetail(parsed.error));
|
|
105
109
|
}
|
|
106
110
|
for (const thread of extractThreads(parsed.data)) {
|
|
107
111
|
validateThreadIdentity(thread, identities);
|
|
112
|
+
sawActive ||= statusType(thread.status) === 'active';
|
|
108
113
|
}
|
|
109
|
-
|
|
114
|
+
const appServer = normalizeAppServer([line]);
|
|
115
|
+
complete ||= appServer.complete;
|
|
116
|
+
normalized = appServer.records;
|
|
110
117
|
}
|
|
111
118
|
else {
|
|
112
119
|
const parsed = RolloutEnvelopeSchema.safeParse(line.value);
|
|
113
120
|
if (!parsed.success) {
|
|
114
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex rollout record is malformed');
|
|
115
|
-
}
|
|
116
|
-
const payload = asObject(parsed.data.payload);
|
|
117
|
-
if (parsed.data.type === 'session_meta') {
|
|
118
|
-
const declaredId = stringValue(payload.id);
|
|
119
|
-
if (!declaredId) {
|
|
120
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex session metadata is missing its id');
|
|
121
|
-
}
|
|
122
|
-
nativeSessionId = declaredId;
|
|
121
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex rollout record is malformed', schemaRejectionDetail(parsed.error));
|
|
123
122
|
}
|
|
124
|
-
|
|
125
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex rollout has no session identity before content records');
|
|
126
|
-
}
|
|
127
|
-
normalized = [rolloutRecord(nativeSessionId, parsed.data, payload, line, sourceArtifactSessionId)];
|
|
123
|
+
normalized = [normalizeRolloutRecord(rolloutState, parsed.data, line, sourceArtifactSessionId)];
|
|
128
124
|
}
|
|
129
125
|
for (const record of normalized) {
|
|
130
126
|
if (outputIndex++ >= start)
|
|
@@ -132,25 +128,12 @@ export class CodexSessionAdapter {
|
|
|
132
128
|
}
|
|
133
129
|
}
|
|
134
130
|
if (!sawRecord && !input.incompleteTail) {
|
|
135
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex JSONL source is empty');
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
async readSource(source) {
|
|
139
|
-
const input = await readBoundedJsonLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
|
|
140
|
-
if (input.records.length === 0 && !input.incompleteTail) {
|
|
141
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex JSONL source is empty');
|
|
131
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex JSONL source is empty', schemaIssueDetail('<root>'));
|
|
142
132
|
}
|
|
143
|
-
const appServer = isAppServerLocator(source.locator)
|
|
144
|
-
|| input.records.some((record) => isAppServerRecord(record.value));
|
|
145
|
-
const schemaVersion = declaredSchemaVersion(input.records);
|
|
146
|
-
assertSupportedSchemaMajor(schemaVersion);
|
|
147
|
-
const normalized = appServer
|
|
148
|
-
? normalizeAppServer(input.records)
|
|
149
|
-
: normalizeRollout(input.records, source.locator);
|
|
150
133
|
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
134
|
+
sourceSchemaVersion: schemaVersion ?? CODEX_SOURCE_SCHEMA_VERSION,
|
|
135
|
+
consistency: complete && !sawActive ? 'complete' : 'provisional',
|
|
136
|
+
operationalState: 'available',
|
|
154
137
|
};
|
|
155
138
|
}
|
|
156
139
|
}
|
|
@@ -162,7 +145,7 @@ function normalizeAppServer(input) {
|
|
|
162
145
|
for (const line of input) {
|
|
163
146
|
const parsed = AppServerEnvelopeSchema.safeParse(line.value);
|
|
164
147
|
if (!parsed.success) {
|
|
165
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex App Server record is malformed');
|
|
148
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex App Server record is malformed', schemaRejectionDetail(parsed.error));
|
|
166
149
|
}
|
|
167
150
|
const envelope = parsed.data;
|
|
168
151
|
const threads = extractThreads(envelope);
|
|
@@ -207,30 +190,62 @@ function normalizeAppServer(input) {
|
|
|
207
190
|
}
|
|
208
191
|
return { records, complete: complete && !sawActive };
|
|
209
192
|
}
|
|
210
|
-
function
|
|
211
|
-
const
|
|
212
|
-
let
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex rollout record is malformed');
|
|
193
|
+
function normalizeRolloutRecord(state, envelope, line, sourceArtifactSessionId) {
|
|
194
|
+
const payload = asObject(envelope.payload);
|
|
195
|
+
let firstMetadata = false;
|
|
196
|
+
if (envelope.type === 'session_meta') {
|
|
197
|
+
const declaredId = stringValue(payload.id);
|
|
198
|
+
if (!declaredId) {
|
|
199
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex session metadata is missing its id', schemaIssueDetail('payload.id'));
|
|
218
200
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
201
|
+
if (!state.metadataId) {
|
|
202
|
+
firstMetadata = true;
|
|
203
|
+
state.metadataId = declaredId;
|
|
204
|
+
state.nativeSessionId = declaredId;
|
|
205
|
+
const spawn = asObject(asObject(asObject(payload.source).subagent).thread_spawn);
|
|
206
|
+
const lineage = Object.fromEntries(Object.entries({
|
|
207
|
+
forkedFromId: stringValue(payload.forked_from_id),
|
|
208
|
+
parentThreadId: stringValue(spawn.parent_thread_id),
|
|
209
|
+
depth: nonnegativeInteger(spawn.depth ?? payload.depth),
|
|
210
|
+
agentPath: stringValue(payload.agent_path ?? spawn.agent_path),
|
|
211
|
+
agentNickname: stringValue(payload.agent_nickname ?? spawn.agent_nickname),
|
|
212
|
+
subagentHistoryStartOrdinal: nonnegativeInteger(payload.subagent_history_start_ordinal),
|
|
213
|
+
threadSource: stringValue(payload.thread_source),
|
|
214
|
+
}).filter(([, value]) => value !== undefined));
|
|
215
|
+
if (Object.keys(lineage).length > 0)
|
|
216
|
+
state.lineage = lineage;
|
|
227
217
|
}
|
|
228
|
-
if (
|
|
229
|
-
|
|
218
|
+
else if (declaredId !== state.metadataId) {
|
|
219
|
+
state.replayParentId ??= declaredId;
|
|
230
220
|
}
|
|
231
|
-
records.push(rolloutRecord(nativeSessionId, envelope, payload, line, sourceArtifactSessionId));
|
|
232
221
|
}
|
|
233
|
-
|
|
222
|
+
if (!state.nativeSessionId) {
|
|
223
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex rollout has no session identity before content records', schemaIssueDetail('payload.id'));
|
|
224
|
+
}
|
|
225
|
+
const historyStart = state.lineage?.subagentHistoryStartOrdinal;
|
|
226
|
+
// Legacy rollouts have no ordinal boundary; a new turn ends the replay prefix.
|
|
227
|
+
if (historyStart === undefined && (envelope.type === 'turn_context'
|
|
228
|
+
|| (envelope.type === 'event_msg' && payload.type === 'task_started'))) {
|
|
229
|
+
state.replayParentId = undefined;
|
|
230
|
+
}
|
|
231
|
+
const parentId = stringValue(state.lineage?.parentThreadId)
|
|
232
|
+
?? stringValue(state.lineage?.forkedFromId)
|
|
233
|
+
?? state.replayParentId;
|
|
234
|
+
const inherited = !firstMetadata && parentId && (typeof historyStart === 'number'
|
|
235
|
+
? (nonnegativeInteger(envelope.ordinal) ?? line.sequence) < historyStart
|
|
236
|
+
: state.replayParentId !== undefined);
|
|
237
|
+
const record = rolloutRecord(state.nativeSessionId, envelope, payload, line, sourceArtifactSessionId);
|
|
238
|
+
if (state.lineage)
|
|
239
|
+
record.extensions.lineage = state.lineage;
|
|
240
|
+
if (inherited) {
|
|
241
|
+
record.extensions.inheritedFromId = parentId;
|
|
242
|
+
record.extensions.replayed = true;
|
|
243
|
+
}
|
|
244
|
+
return record;
|
|
245
|
+
}
|
|
246
|
+
function nonnegativeInteger(value) {
|
|
247
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0
|
|
248
|
+
? value : undefined;
|
|
234
249
|
}
|
|
235
250
|
function threadEvidence(thread, envelope, line) {
|
|
236
251
|
const status = statusType(thread.status);
|
|
@@ -350,7 +365,7 @@ function extractThreads(envelope) {
|
|
|
350
365
|
return candidates.map((candidate) => {
|
|
351
366
|
const parsed = ThreadSchema.safeParse(candidate);
|
|
352
367
|
if (!parsed.success) {
|
|
353
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Codex App Server thread is malformed');
|
|
368
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Codex App Server thread is malformed', schemaRejectionDetail(parsed.error));
|
|
354
369
|
}
|
|
355
370
|
return parsed.data;
|
|
356
371
|
});
|
|
@@ -359,7 +374,7 @@ function validateThreadIdentity(thread, identities) {
|
|
|
359
374
|
const sessionTree = thread.sessionId ?? thread.id;
|
|
360
375
|
const previous = identities.get(thread.id);
|
|
361
376
|
if (previous && previous !== sessionTree) {
|
|
362
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'Codex thread session-tree identity changed');
|
|
377
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Codex thread session-tree identity changed', schemaIssueDetail('sessionId'));
|
|
363
378
|
}
|
|
364
379
|
identities.set(thread.id, sessionTree);
|
|
365
380
|
}
|
|
@@ -514,7 +529,7 @@ function parseCursor(value) {
|
|
|
514
529
|
if (value === undefined || value === '')
|
|
515
530
|
return 0;
|
|
516
531
|
if (!/^\d+$/.test(value))
|
|
517
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'Codex record cursor is invalid');
|
|
532
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Codex record cursor is invalid', schemaIssueDetail('cursor'));
|
|
518
533
|
return Number(value);
|
|
519
534
|
}
|
|
520
535
|
function unixTimestamp(value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename, extname } from 'node:path';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
4
4
|
import { readBoundedJson } from '../readers.js';
|
|
5
5
|
export const COPILOT_ADAPTER_VERSION = '1.0.0';
|
|
6
6
|
export const COPILOT_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
@@ -76,7 +76,7 @@ export class CopilotSessionAdapter {
|
|
|
76
76
|
}, this.limits);
|
|
77
77
|
const parsed = ExportSchema.safeParse(value);
|
|
78
78
|
if (!parsed.success) {
|
|
79
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Copilot chat export is malformed');
|
|
79
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Copilot chat export is malformed', schemaRejectionDetail(parsed.error));
|
|
80
80
|
}
|
|
81
81
|
const schemaVersion = declaredSchemaVersion(parsed.data);
|
|
82
82
|
assertSupportedSchemaMajor(schemaVersion);
|
|
@@ -193,7 +193,7 @@ function declaredSchemaVersion(value) {
|
|
|
193
193
|
? value.version
|
|
194
194
|
: Number(String(value.version).split('.')[0]);
|
|
195
195
|
if (!Number.isInteger(major) || major < 1) {
|
|
196
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Copilot export version');
|
|
196
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Copilot export version', schemaIssueDetail('version'));
|
|
197
197
|
}
|
|
198
198
|
return `${major}.0.0`;
|
|
199
199
|
}
|
|
@@ -210,7 +210,7 @@ function parseCursor(value) {
|
|
|
210
210
|
if (!value)
|
|
211
211
|
return 0;
|
|
212
212
|
if (!/^\d+$/.test(value)) {
|
|
213
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Copilot record cursor');
|
|
213
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Copilot record cursor', schemaIssueDetail('cursor'));
|
|
214
214
|
}
|
|
215
215
|
return Number(value);
|
|
216
216
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { opendir } from 'node:fs/promises';
|
|
2
2
|
import { basename, dirname, extname, resolve, } from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
5
|
-
import {
|
|
4
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
5
|
+
import { readBoundedText, streamBoundedJsonLines, } from '../readers.js';
|
|
6
6
|
export const CURSOR_ADAPTER_VERSION = '1.0.0';
|
|
7
7
|
export const CURSOR_SOURCE_SCHEMA_VERSION = '1.0.0';
|
|
8
8
|
const CliEventSchema = z.object({
|
|
@@ -82,7 +82,7 @@ export class CursorSessionAdapter {
|
|
|
82
82
|
for (const root of [...scope.allowedRoots].sort()) {
|
|
83
83
|
for await (const locator of discoverJsonl(resolve(root), this.discoveryLimits.maxDepth)) {
|
|
84
84
|
if (++emitted > this.discoveryLimits.maxFiles) {
|
|
85
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Cursor source discovery exceeded the authorized file limit');
|
|
85
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Cursor source discovery exceeded the authorized file limit', { limit: 'maxFiles', configured: this.discoveryLimits.maxFiles, observed: emitted });
|
|
86
86
|
}
|
|
87
87
|
yield {
|
|
88
88
|
provider: 'cursor',
|
|
@@ -93,6 +93,16 @@ export class CursorSessionAdapter {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async inspect(source) {
|
|
96
|
+
if (source.locatorClass === 'cursor-cli-stream-json'
|
|
97
|
+
|| source.locatorClass === 'cursor-cloud-events-jsonl'
|
|
98
|
+
|| source.locatorClass === 'cursor-agent-transcript-jsonl') {
|
|
99
|
+
const records = this.stream(source);
|
|
100
|
+
let result = await records.next();
|
|
101
|
+
while (!result.done)
|
|
102
|
+
result = await records.next();
|
|
103
|
+
if (result.value)
|
|
104
|
+
return result.value;
|
|
105
|
+
}
|
|
96
106
|
const parsed = await this.readSource(source);
|
|
97
107
|
return {
|
|
98
108
|
sourceSchemaVersion: parsed.schemaVersion,
|
|
@@ -102,7 +112,8 @@ export class CursorSessionAdapter {
|
|
|
102
112
|
}
|
|
103
113
|
async *stream(source, cursor) {
|
|
104
114
|
if (source.locatorClass === 'cursor-cli-stream-json'
|
|
105
|
-
|| source.locatorClass === 'cursor-cloud-events-jsonl'
|
|
115
|
+
|| source.locatorClass === 'cursor-cloud-events-jsonl'
|
|
116
|
+
|| source.locatorClass === 'cursor-agent-transcript-jsonl') {
|
|
106
117
|
const input = await streamBoundedJsonLines({
|
|
107
118
|
selectedPath: source.locator,
|
|
108
119
|
allowedRoots: source.authorizedScope.allowedRoots,
|
|
@@ -114,13 +125,14 @@ export class CursorSessionAdapter {
|
|
|
114
125
|
let agentId;
|
|
115
126
|
let runId;
|
|
116
127
|
let sawRecord = false;
|
|
128
|
+
let complete = false;
|
|
117
129
|
for await (const line of input) {
|
|
118
130
|
sawRecord = true;
|
|
119
131
|
const raw = asObject(line.value);
|
|
120
132
|
const currentSchema = typeof raw.schemaVersion === 'string'
|
|
121
133
|
? raw.schemaVersion : CURSOR_SOURCE_SCHEMA_VERSION;
|
|
122
134
|
if (schemaVersion && schemaVersion !== currentSchema) {
|
|
123
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions');
|
|
135
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions', schemaIssueDetail('schemaVersion'));
|
|
124
136
|
}
|
|
125
137
|
schemaVersion = currentSchema;
|
|
126
138
|
assertSupportedSchemaMajor(schemaVersion);
|
|
@@ -128,7 +140,7 @@ export class CursorSessionAdapter {
|
|
|
128
140
|
if (source.locatorClass === 'cursor-cli-stream-json') {
|
|
129
141
|
const parsed = CliEventSchema.safeParse(line.value);
|
|
130
142
|
if (!parsed.success) {
|
|
131
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed');
|
|
143
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed', schemaRejectionDetail(parsed.error));
|
|
132
144
|
}
|
|
133
145
|
const event = parsed.data;
|
|
134
146
|
if (cliSessionId && cliSessionId !== event.session_id) {
|
|
@@ -137,10 +149,10 @@ export class CursorSessionAdapter {
|
|
|
137
149
|
cliSessionId = event.session_id;
|
|
138
150
|
record = normalizeCli([line]).records[0];
|
|
139
151
|
}
|
|
140
|
-
else {
|
|
152
|
+
else if (source.locatorClass === 'cursor-cloud-events-jsonl') {
|
|
141
153
|
const parsed = CloudEventSchema.safeParse(line.value);
|
|
142
154
|
if (!parsed.success) {
|
|
143
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed');
|
|
155
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed', schemaRejectionDetail(parsed.error));
|
|
144
156
|
}
|
|
145
157
|
const event = parsed.data;
|
|
146
158
|
const nextAgentId = event.agent?.id ?? agentId;
|
|
@@ -153,27 +165,38 @@ export class CursorSessionAdapter {
|
|
|
153
165
|
}
|
|
154
166
|
agentId = nextAgentId;
|
|
155
167
|
runId = nextRunId;
|
|
156
|
-
|
|
168
|
+
const normalized = normalizeCloud([{
|
|
157
169
|
...line,
|
|
158
170
|
value: {
|
|
159
171
|
...event,
|
|
160
172
|
agent: event.agent ?? (agentId ? { id: agentId } : undefined),
|
|
161
173
|
run: event.run ?? (runId ? { id: runId } : undefined),
|
|
162
174
|
},
|
|
163
|
-
}])
|
|
175
|
+
}]);
|
|
176
|
+
complete ||= normalized.complete;
|
|
177
|
+
record = normalized.records[0];
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
record = normalizeAgentTranscript([line], source.locator).records[0];
|
|
164
181
|
}
|
|
182
|
+
complete ||= record.extensions?.lifecycle === 'complete';
|
|
165
183
|
if (outputIndex++ >= start)
|
|
166
184
|
yield record;
|
|
167
185
|
}
|
|
168
186
|
if (!sawRecord && !input.incompleteTail) {
|
|
169
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty');
|
|
187
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty', schemaIssueDetail('<root>'));
|
|
170
188
|
}
|
|
171
|
-
return
|
|
189
|
+
return {
|
|
190
|
+
sourceSchemaVersion: schemaVersion ?? CURSOR_SOURCE_SCHEMA_VERSION,
|
|
191
|
+
consistency: complete && !input.incompleteTail ? 'complete' : 'provisional',
|
|
192
|
+
operationalState: 'available',
|
|
193
|
+
};
|
|
172
194
|
}
|
|
173
195
|
const parsed = await this.readSource(source);
|
|
174
196
|
const start = parseCursor(cursor?.value);
|
|
175
197
|
for (const record of parsed.records.slice(start))
|
|
176
198
|
yield record;
|
|
199
|
+
return undefined;
|
|
177
200
|
}
|
|
178
201
|
async readSource(source) {
|
|
179
202
|
const authorization = {
|
|
@@ -191,36 +214,6 @@ export class CursorSessionAdapter {
|
|
|
191
214
|
records: normalizeMarkdown(input.value, source.locator),
|
|
192
215
|
};
|
|
193
216
|
}
|
|
194
|
-
const input = await readBoundedJsonLines(authorization, { consistency: 'provisional', limits: this.limits });
|
|
195
|
-
if (input.records.length === 0 && !input.incompleteTail) {
|
|
196
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor structured source is empty');
|
|
197
|
-
}
|
|
198
|
-
const schemaVersion = declaredVersion(input.records);
|
|
199
|
-
assertSupportedSchemaMajor(schemaVersion);
|
|
200
|
-
if (source.locatorClass === 'cursor-cli-stream-json') {
|
|
201
|
-
const normalized = normalizeCli(input.records);
|
|
202
|
-
return {
|
|
203
|
-
schemaVersion,
|
|
204
|
-
consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
|
|
205
|
-
records: normalized.records,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
if (source.locatorClass === 'cursor-cloud-events-jsonl') {
|
|
209
|
-
const normalized = normalizeCloud(input.records);
|
|
210
|
-
return {
|
|
211
|
-
schemaVersion,
|
|
212
|
-
consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
|
|
213
|
-
records: normalized.records,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
if (source.locatorClass === 'cursor-agent-transcript-jsonl') {
|
|
217
|
-
const normalized = normalizeAgentTranscript(input.records, source.locator);
|
|
218
|
-
return {
|
|
219
|
-
schemaVersion,
|
|
220
|
-
consistency: normalized.complete && !input.incompleteTail ? 'complete' : 'provisional',
|
|
221
|
-
records: normalized.records,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
217
|
throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported Cursor source class');
|
|
225
218
|
}
|
|
226
219
|
}
|
|
@@ -257,7 +250,7 @@ function normalizeCli(input) {
|
|
|
257
250
|
for (const line of input) {
|
|
258
251
|
const parsed = CliEventSchema.safeParse(line.value);
|
|
259
252
|
if (!parsed.success) {
|
|
260
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed');
|
|
253
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor CLI event is malformed', schemaRejectionDetail(parsed.error));
|
|
261
254
|
}
|
|
262
255
|
const event = parsed.data;
|
|
263
256
|
if (sessionId && sessionId !== event.session_id) {
|
|
@@ -307,7 +300,7 @@ function normalizeCloud(input) {
|
|
|
307
300
|
for (const line of input) {
|
|
308
301
|
const parsed = CloudEventSchema.safeParse(line.value);
|
|
309
302
|
if (!parsed.success) {
|
|
310
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed');
|
|
303
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Cloud Agent event is malformed', schemaRejectionDetail(parsed.error));
|
|
311
304
|
}
|
|
312
305
|
const event = parsed.data;
|
|
313
306
|
const nextAgentId = event.agent?.id ?? agentId;
|
|
@@ -371,7 +364,7 @@ function normalizeAgentTranscript(input, locator) {
|
|
|
371
364
|
for (const line of input) {
|
|
372
365
|
const parsed = AgentTranscriptEventSchema.safeParse(line.value);
|
|
373
366
|
if (!parsed.success) {
|
|
374
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor agent transcript event is malformed');
|
|
367
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor agent transcript event is malformed', schemaRejectionDetail(parsed.error));
|
|
375
368
|
}
|
|
376
369
|
const event = parsed.data;
|
|
377
370
|
const terminal = event.type === 'turn_ended' && event.status === 'success';
|
|
@@ -411,7 +404,7 @@ function normalizeMarkdown(value, locator) {
|
|
|
411
404
|
const heading = /^#{1,3}\s+(User|Assistant|Cursor)\s*$/gim;
|
|
412
405
|
const matches = [...value.matchAll(heading)];
|
|
413
406
|
if (matches.length === 0) {
|
|
414
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Markdown export has no role headings');
|
|
407
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Cursor Markdown export has no role headings', schemaIssueDetail('headings'));
|
|
415
408
|
}
|
|
416
409
|
const nativeSessionId = basename(locator, extname(locator));
|
|
417
410
|
return matches.map((match, index) => {
|
|
@@ -462,16 +455,6 @@ function messageText(value) {
|
|
|
462
455
|
return '';
|
|
463
456
|
}).filter(Boolean).join('\n');
|
|
464
457
|
}
|
|
465
|
-
function declaredVersion(records) {
|
|
466
|
-
const versions = new Set(records.map((line) => {
|
|
467
|
-
const value = asObject(line.value);
|
|
468
|
-
return typeof value.schemaVersion === 'string' ? value.schemaVersion : CURSOR_SOURCE_SCHEMA_VERSION;
|
|
469
|
-
}));
|
|
470
|
-
if (versions.size !== 1) {
|
|
471
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'Cursor source declares mixed schema versions');
|
|
472
|
-
}
|
|
473
|
-
return [...versions][0];
|
|
474
|
-
}
|
|
475
458
|
function declaredEventVersion(event) {
|
|
476
459
|
return event.schemaVersion ?? CURSOR_SOURCE_SCHEMA_VERSION;
|
|
477
460
|
}
|
|
@@ -511,7 +494,7 @@ function parseCursor(value) {
|
|
|
511
494
|
if (!value)
|
|
512
495
|
return 0;
|
|
513
496
|
if (!/^\d+$/.test(value)) {
|
|
514
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Cursor record cursor');
|
|
497
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Cursor record cursor', schemaIssueDetail('cursor'));
|
|
515
498
|
}
|
|
516
499
|
return Number(value);
|
|
517
500
|
}
|
|
@@ -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 DEEPSEEK_HARNESS_ADAPTER_VERSION = '1.0.0';
|
|
7
7
|
export const DEEPSEEK_HARNESS_SOURCE_SCHEMA_VERSION = '2.0.0';
|
|
8
8
|
const Header = z.object({
|
|
@@ -39,7 +39,7 @@ export class DeepSeekHarnessSessionAdapter {
|
|
|
39
39
|
for (const root of [...scope.allowedRoots].sort()) {
|
|
40
40
|
for await (const locator of jsonlFiles(resolve(root))) {
|
|
41
41
|
if (++count > this.maxFiles)
|
|
42
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'DeepSeek Harness discovery file limit exceeded');
|
|
42
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'DeepSeek Harness discovery file limit exceeded', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
|
|
43
43
|
yield { provider: this.provider, locator, locatorClass: 'deepseek-harness-session-v2-jsonl' };
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -47,12 +47,15 @@ export class DeepSeekHarnessSessionAdapter {
|
|
|
47
47
|
async inspect(source) {
|
|
48
48
|
let input;
|
|
49
49
|
try {
|
|
50
|
-
input = await
|
|
50
|
+
input = await streamBoundedJsonLines({ selectedPath: source.locator, allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
|
|
51
51
|
}
|
|
52
52
|
catch (error) {
|
|
53
53
|
throw normalizeAuthorizationError(error);
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
let header;
|
|
56
|
+
for await (const line of input)
|
|
57
|
+
header ??= parseHeader(line.value);
|
|
58
|
+
header ??= parseHeader(undefined);
|
|
56
59
|
if (input.incompleteTail)
|
|
57
60
|
throw new SessionContractError('TRUNCATED_SOURCE', 'DeepSeek Harness session has a truncated JSONL tail');
|
|
58
61
|
return { sourceSchemaVersion: `${header.version}.0.0`, consistency: 'complete', operationalState: 'available' };
|
|
@@ -67,24 +70,24 @@ export class DeepSeekHarnessSessionAdapter {
|
|
|
67
70
|
}
|
|
68
71
|
const start = cursor?.value.replace(/^byte:/, '') ?? '0';
|
|
69
72
|
if (!/^\d+$/.test(start))
|
|
70
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid DeepSeek Harness cursor');
|
|
73
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid DeepSeek Harness cursor', schemaIssueDetail('cursor'));
|
|
71
74
|
let header;
|
|
72
75
|
let expectedSeq = 0;
|
|
73
76
|
let end = 0;
|
|
74
77
|
for await (const line of input) {
|
|
75
78
|
end = line.byteOffset + line.byteLength;
|
|
76
79
|
if (Number(start) > line.byteOffset && Number(start) < end)
|
|
77
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'DeepSeek Harness cursor is not a record boundary');
|
|
80
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'DeepSeek Harness cursor is not a record boundary', schemaIssueDetail('cursor'));
|
|
78
81
|
if (!header) {
|
|
79
82
|
header = parseHeader(line.value);
|
|
80
83
|
continue;
|
|
81
84
|
}
|
|
82
85
|
const parsed = Event.safeParse(line.value);
|
|
83
86
|
if (!parsed.success)
|
|
84
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'DeepSeek Harness session event is malformed');
|
|
87
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'DeepSeek Harness session event is malformed', schemaRejectionDetail(parsed.error));
|
|
85
88
|
const event = parsed.data;
|
|
86
89
|
if (event.seq !== expectedSeq)
|
|
87
|
-
throw new SessionContractError(event.seq < expectedSeq ? 'DUPLICATE_NATIVE_ID' : 'SCHEMA_DRIFT', `DeepSeek Harness sequence expected ${expectedSeq}, received ${event.seq}
|
|
90
|
+
throw new SessionContractError(event.seq < expectedSeq ? 'DUPLICATE_NATIVE_ID' : 'SCHEMA_DRIFT', `DeepSeek Harness sequence expected ${expectedSeq}, received ${event.seq}`, schemaIssueDetail('seq'));
|
|
88
91
|
expectedSeq++;
|
|
89
92
|
if (end <= Number(start))
|
|
90
93
|
continue;
|
|
@@ -121,9 +124,9 @@ export class DeepSeekHarnessSessionAdapter {
|
|
|
121
124
|
};
|
|
122
125
|
}
|
|
123
126
|
if (!header)
|
|
124
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'DeepSeek Harness session header is missing');
|
|
127
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'DeepSeek Harness session header is missing', schemaIssueDetail('header'));
|
|
125
128
|
if (Number(start) > end)
|
|
126
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'DeepSeek Harness cursor exceeds source size');
|
|
129
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'DeepSeek Harness cursor exceeds source size', schemaIssueDetail('cursor'));
|
|
127
130
|
if (input.incompleteTail)
|
|
128
131
|
throw new SessionContractError('TRUNCATED_SOURCE', 'DeepSeek Harness session has a truncated JSONL tail');
|
|
129
132
|
}
|
|
@@ -131,7 +134,7 @@ export class DeepSeekHarnessSessionAdapter {
|
|
|
131
134
|
function parseHeader(value) {
|
|
132
135
|
const parsed = Header.safeParse(value);
|
|
133
136
|
if (!parsed.success)
|
|
134
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'DeepSeek Harness session header is malformed');
|
|
137
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'DeepSeek Harness session header is malformed', schemaRejectionDetail(parsed.error));
|
|
135
138
|
if (parsed.data.version !== 2)
|
|
136
139
|
throw new SessionContractError('UNKNOWN_SCHEMA_MAJOR', `unsupported DeepSeek Harness session version: ${parsed.data.version}`);
|
|
137
140
|
return parsed.data;
|