@aiwg/cli 2026.10.3 → 2026.10.5
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 +69 -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 +286 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +130 -0
- package/dist/src/artifacts/index-refresh.js +591 -0
- package/dist/src/artifacts/index-worker.mjs +11 -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 +137 -43
- 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 +95 -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 +252 -12
- package/dist/src/sessions/contracts.js +50 -3
- package/dist/src/sessions/coverage-preflight.js +27 -2
- 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 +145 -26
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +105 -0
- package/dist/src/sessions/repository.js +151 -54
- package/dist/src/sessions/workspace-discovery.js +292 -60
- 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,7 +1,7 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
4
|
-
import {
|
|
3
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
4
|
+
import { streamBoundedJsonLines, } from '../readers.js';
|
|
5
5
|
export const DEVIN_DESKTOP_ADAPTER_VERSION = '1.1.0';
|
|
6
6
|
export const DEVIN_DESKTOP_TRANSCRIPT_SCHEMA_VERSION = '1.0.0';
|
|
7
7
|
/** @deprecated Compatibility export. */
|
|
@@ -38,19 +38,14 @@ export class DevinDesktopSessionAdapter {
|
|
|
38
38
|
// Deliberately empty: hook enablement and transcript selection are explicit user actions.
|
|
39
39
|
}
|
|
40
40
|
async inspect(source) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
41
|
+
const records = this.stream(source);
|
|
42
|
+
let result = await records.next();
|
|
43
|
+
while (!result.done)
|
|
44
|
+
result = await records.next();
|
|
45
|
+
return result.value;
|
|
47
46
|
}
|
|
48
47
|
async *stream(source, cursor) {
|
|
49
|
-
|
|
50
|
-
// Preserve the precise unsupported-operation diagnostics.
|
|
51
|
-
await this.readSource(source);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
48
|
+
this.assertSupportedLocator(source);
|
|
54
49
|
const start = parseCursor(cursor?.value);
|
|
55
50
|
const input = await streamBoundedJsonLines({
|
|
56
51
|
selectedPath: source.locator,
|
|
@@ -62,12 +57,12 @@ export class DevinDesktopSessionAdapter {
|
|
|
62
57
|
for await (const line of input) {
|
|
63
58
|
const parsed = StepSchema.safeParse(line.value);
|
|
64
59
|
if (!parsed.success) {
|
|
65
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Windsurf transcript step is malformed');
|
|
60
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Windsurf transcript step is malformed', schemaRejectionDetail(parsed.error));
|
|
66
61
|
}
|
|
67
62
|
const step = parsed.data;
|
|
68
63
|
const currentVersion = step.schema_version ?? DEVIN_DESKTOP_TRANSCRIPT_SCHEMA_VERSION;
|
|
69
64
|
if (schemaVersion && schemaVersion !== currentVersion) {
|
|
70
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed Windsurf transcript schemas');
|
|
65
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed Windsurf transcript schemas', schemaIssueDetail('schema_version'));
|
|
71
66
|
}
|
|
72
67
|
schemaVersion = currentVersion;
|
|
73
68
|
assertSupportedSchemaMajor(schemaVersion);
|
|
@@ -75,7 +70,7 @@ export class DevinDesktopSessionAdapter {
|
|
|
75
70
|
?? nativeSessionId
|
|
76
71
|
?? basename(source.locator, '.jsonl');
|
|
77
72
|
if (step.trajectory_id && nativeSessionId && step.trajectory_id !== nativeSessionId) {
|
|
78
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed Windsurf trajectory identities');
|
|
73
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed Windsurf trajectory identities', schemaIssueDetail('trajectory_id'));
|
|
79
74
|
}
|
|
80
75
|
nativeSessionId = currentSession;
|
|
81
76
|
if (seen++ < start)
|
|
@@ -83,45 +78,21 @@ export class DevinDesktopSessionAdapter {
|
|
|
83
78
|
yield normalizeStep(step, currentSession, line.sequence, line.byteOffset, canonicalLocatorClass(source.locatorClass), schemaVersion);
|
|
84
79
|
}
|
|
85
80
|
if (seen === 0) {
|
|
86
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Windsurf transcript is empty or malformed');
|
|
81
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Windsurf transcript is empty or malformed', schemaIssueDetail('<root>'));
|
|
87
82
|
}
|
|
83
|
+
return {
|
|
84
|
+
sourceSchemaVersion: schemaVersion,
|
|
85
|
+
consistency: 'provisional',
|
|
86
|
+
operationalState: 'available',
|
|
87
|
+
};
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
assertSupportedLocator(source) {
|
|
90
90
|
if (isLegacyLocator(source.locatorClass)) {
|
|
91
91
|
throw new SessionContractError('UNSUPPORTED_OPERATION', 'legacy Devin Desktop/Windsurf protobuf stores are unsupported; enable post_cascade_response_with_transcript and select its JSONL output');
|
|
92
92
|
}
|
|
93
93
|
if (!isCurrentHookLocator(source.locatorClass)) {
|
|
94
94
|
throw new SessionContractError('UNSUPPORTED_OPERATION', 'Devin Desktop (Windsurf compatibility) requires an explicitly selected post_cascade_response_with_transcript JSONL file');
|
|
95
95
|
}
|
|
96
|
-
const input = await readBoundedJsonLines({
|
|
97
|
-
selectedPath: source.locator,
|
|
98
|
-
allowedRoots: source.authorizedScope.allowedRoots,
|
|
99
|
-
}, { consistency: 'provisional', limits: this.limits });
|
|
100
|
-
if (input.records.length === 0) {
|
|
101
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Windsurf transcript is empty or malformed');
|
|
102
|
-
}
|
|
103
|
-
const steps = input.records.map((record) => {
|
|
104
|
-
const parsed = StepSchema.safeParse(record.value);
|
|
105
|
-
if (!parsed.success) {
|
|
106
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Windsurf transcript step is malformed');
|
|
107
|
-
}
|
|
108
|
-
return { ...record, value: parsed.data };
|
|
109
|
-
});
|
|
110
|
-
const versions = new Set(steps.map(({ value }) => value.schema_version ?? DEVIN_DESKTOP_TRANSCRIPT_SCHEMA_VERSION));
|
|
111
|
-
if (versions.size !== 1) {
|
|
112
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed Windsurf transcript schemas');
|
|
113
|
-
}
|
|
114
|
-
const schemaVersion = [...versions][0];
|
|
115
|
-
assertSupportedSchemaMajor(schemaVersion);
|
|
116
|
-
const trajectoryIds = new Set(steps.flatMap(({ value }) => value.trajectory_id ? [value.trajectory_id] : []));
|
|
117
|
-
if (trajectoryIds.size > 1) {
|
|
118
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed Windsurf trajectory identities');
|
|
119
|
-
}
|
|
120
|
-
const nativeSessionId = [...trajectoryIds][0] ?? basename(source.locator, '.jsonl');
|
|
121
|
-
return {
|
|
122
|
-
schemaVersion,
|
|
123
|
-
records: steps.map(({ value, sequence, byteOffset }) => normalizeStep(value, nativeSessionId, sequence, byteOffset, canonicalLocatorClass(source.locatorClass), schemaVersion)),
|
|
124
|
-
};
|
|
125
96
|
}
|
|
126
97
|
}
|
|
127
98
|
/** @deprecated Use DevinDesktopSessionAdapter; retained through the alias window. */
|
|
@@ -206,7 +177,7 @@ function parseCursor(value) {
|
|
|
206
177
|
if (!value)
|
|
207
178
|
return 0;
|
|
208
179
|
if (!/^\d+$/.test(value))
|
|
209
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Windsurf cursor');
|
|
180
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Windsurf cursor', schemaIssueDetail('cursor'));
|
|
210
181
|
return Number(value);
|
|
211
182
|
}
|
|
212
183
|
//# sourceMappingURL=windsurf.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { reconcileCoveragePreflight } from './coverage-preflight.js';
|
|
2
|
-
export const BATCH_IMPORT_VERSION = '
|
|
3
|
-
export const COVERAGE_VERSION = '
|
|
2
|
+
export const BATCH_IMPORT_VERSION = '2.0.0';
|
|
3
|
+
export const COVERAGE_VERSION = '2.0.0';
|
|
4
4
|
export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 * 60 * 60 * 1_000) {
|
|
5
5
|
if (!run)
|
|
6
6
|
return unknownCoverage();
|
|
@@ -9,6 +9,7 @@ export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 *
|
|
|
9
9
|
]);
|
|
10
10
|
const accepted = run.sources.filter((source) => source.status === 'committed').length;
|
|
11
11
|
const rejected = run.sources.filter((source) => source.status === 'rejected').length;
|
|
12
|
+
const excluded = run.sources.filter(source => source.status === 'excluded').length;
|
|
12
13
|
const skipped = run.sources.filter((source) => source.status === 'skipped').length;
|
|
13
14
|
const duplicated = run.sources.filter((source) => source.status === 'duplicate').length;
|
|
14
15
|
const previouslyCommitted = run.sources.filter((source) => source.status === 'previously-committed').length;
|
|
@@ -20,9 +21,12 @@ export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 *
|
|
|
20
21
|
const manifestAgeMs = Math.max(0, now.getTime() - Date.parse(run.manifestCreatedAt));
|
|
21
22
|
const isStale = manifestAgeMs > staleAfterMs;
|
|
22
23
|
const preflight = run.preflight ? reconcileCoveragePreflight(run.preflight, run.sources) : undefined;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const unresolvedProviders = preflight?.unresolvedProviders
|
|
25
|
+
?? [...new Set([...unavailable, ...exportRequired, ...notChecked])].sort();
|
|
26
|
+
const acquisitionIncomplete = rejected > 0 || pending > 0 || skipped > 0
|
|
27
|
+
|| (preflight && ['explicit', 'workspace-config'].includes(preflight.expectedProviderBasis) && unresolvedProviders.length > 0);
|
|
28
|
+
const incomplete = acquisitionIncomplete || !preflight || preflight.status !== 'complete'
|
|
29
|
+
|| unavailable.length > 0 || accepted + duplicated + previouslyCommitted + excluded === 0
|
|
26
30
|
|| exportRequired.length > 0 || notChecked.length > 0;
|
|
27
31
|
const rejectionCounts = {};
|
|
28
32
|
for (const source of run.sources) {
|
|
@@ -54,7 +58,8 @@ export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 *
|
|
|
54
58
|
return {
|
|
55
59
|
schemaVersion: COVERAGE_VERSION,
|
|
56
60
|
status: isStale ? 'stale' : incomplete ? 'partial' : 'complete',
|
|
57
|
-
acquisitionStatus:
|
|
61
|
+
acquisitionStatus: acquisitionIncomplete ? 'partial' : 'complete',
|
|
62
|
+
unresolvedProviders,
|
|
58
63
|
preflight,
|
|
59
64
|
semanticReview: 'not-assessed',
|
|
60
65
|
workspaceId: run.workspaceId,
|
|
@@ -67,6 +72,7 @@ export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 *
|
|
|
67
72
|
discovered: run.sources.length,
|
|
68
73
|
accepted,
|
|
69
74
|
rejected,
|
|
75
|
+
excluded,
|
|
70
76
|
skipped,
|
|
71
77
|
duplicated,
|
|
72
78
|
previouslyCommitted,
|
|
@@ -74,9 +80,9 @@ export function coverageFromBatchRun(run, now = new Date(), staleAfterMs = 24 *
|
|
|
74
80
|
},
|
|
75
81
|
sessionsAccepted: run.sources.reduce((sum, source) => sum + source.sessionsAccepted, 0),
|
|
76
82
|
eventsAccepted: run.sources.reduce((sum, source) => sum + source.eventsAccepted, 0),
|
|
77
|
-
coverageRatio: run.sources.length ===
|
|
83
|
+
coverageRatio: run.sources.length === excluded
|
|
78
84
|
? null
|
|
79
|
-
: (accepted + duplicated + previouslyCommitted) / run.sources.length,
|
|
85
|
+
: (accepted + duplicated + previouslyCommitted) / (run.sources.length - excluded),
|
|
80
86
|
rejectionCounts,
|
|
81
87
|
sourceDateRange: {
|
|
82
88
|
earliest: sourceTimestamps.at(0) ?? null,
|
|
@@ -100,6 +106,7 @@ function unknownCoverage() {
|
|
|
100
106
|
schemaVersion: COVERAGE_VERSION,
|
|
101
107
|
status: 'unknown',
|
|
102
108
|
acquisitionStatus: 'unknown',
|
|
109
|
+
unresolvedProviders: [],
|
|
103
110
|
semanticReview: 'not-assessed',
|
|
104
111
|
workspaceId: '',
|
|
105
112
|
manifestId: null,
|
|
@@ -116,6 +123,7 @@ function unknownCoverage() {
|
|
|
116
123
|
discovered: 0,
|
|
117
124
|
accepted: 0,
|
|
118
125
|
rejected: 0,
|
|
126
|
+
excluded: 0,
|
|
119
127
|
skipped: 0,
|
|
120
128
|
duplicated: 0,
|
|
121
129
|
previouslyCommitted: 0,
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import { stat } from 'node:fs/promises';
|
|
1
|
+
import { mkdtemp, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
2
4
|
import { createSessionAdapterRegistry } from './adapter-registry.js';
|
|
3
5
|
import { coveragePreflight, sessionRuntimeIdentity } from './coverage-preflight.js';
|
|
4
|
-
import { SESSION_CONTRACT_VERSION, SessionContractError, SessionSourceSchema, sha256, } from './contracts.js';
|
|
6
|
+
import { SESSION_CONTRACT_VERSION, SessionContractError, schemaIssueDetail, SessionSourceSchema, sha256, stableSessionId, stableEventId, } from './contracts.js';
|
|
5
7
|
import { BATCH_IMPORT_VERSION, coverageFromBatchRun, } from './batch-contracts.js';
|
|
6
8
|
import { IncrementalSessionImporter, SessionImportFailure } from './importer.js';
|
|
9
|
+
import { matchImportExclusion, resolveImportExclusions } from './import-exclusions.js';
|
|
10
|
+
import { resolveImportLimits } from './import-limits.js';
|
|
7
11
|
import { fingerprintSourceFile } from './readers.js';
|
|
12
|
+
import { SessionRepository } from './repository.js';
|
|
13
|
+
import { redactSessionValue, sanitizeSessionEvent } from './policy.js';
|
|
14
|
+
import { deriveSessionIntent } from './origin.js';
|
|
15
|
+
import { recoverCodexRolloutIdentity } from './adapters/codex.js';
|
|
8
16
|
export async function importDiscoveryManifest(options) {
|
|
17
|
+
if (options.carryForward)
|
|
18
|
+
return rebuildFromCatalog(options);
|
|
19
|
+
const exclusions = resolveImportExclusions(undefined, options.exclude);
|
|
20
|
+
const limits = { ...resolveImportLimits(undefined, {
|
|
21
|
+
maxRecordBytes: options.limits?.maxRecordBytes, maxSourceBytes: options.limits?.maxTotalBytes,
|
|
22
|
+
}), ...options.limits };
|
|
9
23
|
const now = options.now ?? (() => new Date());
|
|
10
24
|
const runId = sha256([
|
|
11
25
|
'workspace-import-v1',
|
|
@@ -17,7 +31,10 @@ export async function importDiscoveryManifest(options) {
|
|
|
17
31
|
if (run.runId !== runId) {
|
|
18
32
|
throw new SessionContractError('IMPORT_CONFLICT', 'discovery manifest is already associated with a different batch run');
|
|
19
33
|
}
|
|
34
|
+
run.schemaVersion = BATCH_IMPORT_VERSION;
|
|
20
35
|
run.preflight = coveragePreflight({
|
|
36
|
+
configuredProviders: options.manifest.preflight?.configuredProviders,
|
|
37
|
+
scopeWarnings: options.manifest.preflight?.scopeWarnings,
|
|
21
38
|
expectedProviders: options.manifest.preflight?.expectedProviders,
|
|
22
39
|
expectedProviderBasis: options.manifest.preflight?.expectedProviderBasis ?? 'legacy',
|
|
23
40
|
runtime: options.runtime ?? sessionRuntimeIdentity(),
|
|
@@ -33,6 +50,12 @@ export async function importDiscoveryManifest(options) {
|
|
|
33
50
|
if (!disposition) {
|
|
34
51
|
throw new SessionContractError('IMPORT_CONFLICT', `batch run is missing discovered source ${manifestSource.sourceId}`);
|
|
35
52
|
}
|
|
53
|
+
const exclusion = matchImportExclusion(manifestSource, exclusions) ?? manifestSource.exclusion;
|
|
54
|
+
if (exclusion) {
|
|
55
|
+
excludeSource(disposition, exclusion, now());
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
delete disposition.exclusion;
|
|
36
59
|
if (isAccepted(disposition.status)) {
|
|
37
60
|
disposition.status = 'previously-committed';
|
|
38
61
|
disposition.updatedAt = now().toISOString();
|
|
@@ -48,6 +71,7 @@ export async function importDiscoveryManifest(options) {
|
|
|
48
71
|
disposition.attempts += 1;
|
|
49
72
|
disposition.errorCode = null;
|
|
50
73
|
disposition.diagnostic = null;
|
|
74
|
+
disposition.detail = null;
|
|
51
75
|
disposition.updatedAt = now().toISOString();
|
|
52
76
|
persistProgress(options.repository, run, now());
|
|
53
77
|
try {
|
|
@@ -62,7 +86,7 @@ export async function importDiscoveryManifest(options) {
|
|
|
62
86
|
allowedRoots: [manifestSource.authorizedRoot],
|
|
63
87
|
},
|
|
64
88
|
};
|
|
65
|
-
const adapter = adapterFor(manifestSource.provider);
|
|
89
|
+
const adapter = adapterFor(manifestSource.provider, limits);
|
|
66
90
|
const source = await sessionSource(selectedSource, adapter);
|
|
67
91
|
const receipts = await new IncrementalSessionImporter(options.repository).import({
|
|
68
92
|
source,
|
|
@@ -74,6 +98,7 @@ export async function importDiscoveryManifest(options) {
|
|
|
74
98
|
publish: false,
|
|
75
99
|
signal: options.signal,
|
|
76
100
|
inactivityThresholdMs: options.inactivityThresholdMs,
|
|
101
|
+
limits,
|
|
77
102
|
});
|
|
78
103
|
await assertManifestSourceUnchanged(manifestSource);
|
|
79
104
|
const totals = options.repository.sourceImportTotals(manifestSource.sourceId, run.runId);
|
|
@@ -89,10 +114,12 @@ export async function importDiscoveryManifest(options) {
|
|
|
89
114
|
catch (error) {
|
|
90
115
|
disposition.status = 'rejected';
|
|
91
116
|
disposition.errorCode = importErrorCode(error);
|
|
117
|
+
disposition.detail = error instanceof SessionContractError ? error.detail ?? null : null;
|
|
92
118
|
disposition.diagnostic = [
|
|
93
119
|
`provider=${manifestSource.provider}`,
|
|
94
120
|
`source=${manifestSource.sourceId}`,
|
|
95
121
|
`code=${disposition.errorCode}`,
|
|
122
|
+
...(disposition.detail ? [`detail=${JSON.stringify(disposition.detail)}`] : []),
|
|
96
123
|
].join(' ');
|
|
97
124
|
}
|
|
98
125
|
disposition.updatedAt = now().toISOString();
|
|
@@ -101,14 +128,14 @@ export async function importDiscoveryManifest(options) {
|
|
|
101
128
|
}
|
|
102
129
|
const rejected = run.sources.some((source) => source.status === 'rejected');
|
|
103
130
|
const pending = run.sources.some((source) => source.status === 'pending' || source.status === 'running');
|
|
104
|
-
const
|
|
131
|
+
const acquisitionIncomplete = coverageFromBatchRun(run, now()).acquisitionStatus !== 'complete';
|
|
105
132
|
const publishable = run.sources
|
|
106
133
|
.filter((source) => isAccepted(source.status))
|
|
107
134
|
.map((source) => source.sourceId);
|
|
108
135
|
injectFault(options, run, 'before-publication');
|
|
109
136
|
options.repository.commitStagedBatch(run.runId, publishable);
|
|
110
137
|
injectFault(options, run, 'after-publication');
|
|
111
|
-
run.status = pending ? 'interrupted' : rejected ||
|
|
138
|
+
run.status = pending ? 'interrupted' : rejected || acquisitionIncomplete ? 'partial' : 'complete';
|
|
112
139
|
run.updatedAt = now().toISOString();
|
|
113
140
|
run.completedAt = pending ? null : run.updatedAt;
|
|
114
141
|
options.repository.saveBatchImportRun(run);
|
|
@@ -128,11 +155,23 @@ function injectFault(options, run, boundary, sourceId) {
|
|
|
128
155
|
throw error;
|
|
129
156
|
}
|
|
130
157
|
}
|
|
131
|
-
export function previewDiscoveryImport(manifest, existing, now = new Date()) {
|
|
158
|
+
export function previewDiscoveryImport(manifest, existing, now = new Date(), exclude = []) {
|
|
132
159
|
const run = existing ?? newBatchRun(manifest, sha256(['workspace-import-v1', manifest.manifestId, manifest.workspaceId].join('\0')), now);
|
|
160
|
+
const exclusions = resolveImportExclusions(undefined, exclude);
|
|
133
161
|
return receiptFor({
|
|
134
162
|
...run,
|
|
163
|
+
sources: run.sources.map(source => {
|
|
164
|
+
const manifestSource = manifest.sources.find(candidate => candidate.sourceId === source.sourceId);
|
|
165
|
+
const exclusion = matchImportExclusion(manifestSource, exclusions) ?? manifestSource.exclusion;
|
|
166
|
+
if (!exclusion)
|
|
167
|
+
return source;
|
|
168
|
+
const disposition = { ...source };
|
|
169
|
+
excludeSource(disposition, exclusion, now);
|
|
170
|
+
return disposition;
|
|
171
|
+
}),
|
|
135
172
|
preflight: coveragePreflight({
|
|
173
|
+
configuredProviders: manifest.preflight?.configuredProviders,
|
|
174
|
+
scopeWarnings: manifest.preflight?.scopeWarnings,
|
|
136
175
|
expectedProviders: manifest.preflight?.expectedProviders,
|
|
137
176
|
expectedProviderBasis: manifest.preflight?.expectedProviderBasis ?? 'legacy',
|
|
138
177
|
reports: manifest.providers,
|
|
@@ -157,7 +196,8 @@ function newBatchRun(manifest, runId, now) {
|
|
|
157
196
|
sources: manifest.sources.map((source) => ({
|
|
158
197
|
sourceId: source.sourceId,
|
|
159
198
|
provider: source.provider,
|
|
160
|
-
status: 'pending',
|
|
199
|
+
status: source.status ?? 'pending',
|
|
200
|
+
...(source.exclusion ? { exclusion: source.exclusion } : {}),
|
|
161
201
|
attempts: 0,
|
|
162
202
|
sessionsAccepted: 0,
|
|
163
203
|
eventsAccepted: 0,
|
|
@@ -173,10 +213,13 @@ function receiptFor(run, now) {
|
|
|
173
213
|
schemaVersion: BATCH_IMPORT_VERSION,
|
|
174
214
|
run,
|
|
175
215
|
coverage,
|
|
216
|
+
interruptedRuns: [],
|
|
217
|
+
...(run.carryForward ? { carryForward: run.carryForward } : {}),
|
|
176
218
|
totals: {
|
|
177
219
|
discovered: coverage.sources.discovered,
|
|
178
220
|
accepted: coverage.sources.accepted,
|
|
179
221
|
rejected: coverage.sources.rejected,
|
|
222
|
+
excluded: coverage.sources.excluded,
|
|
180
223
|
skipped: coverage.sources.skipped,
|
|
181
224
|
duplicated: coverage.sources.duplicated,
|
|
182
225
|
previouslyCommitted: coverage.sources.previouslyCommitted,
|
|
@@ -186,6 +229,16 @@ function receiptFor(run, now) {
|
|
|
186
229
|
},
|
|
187
230
|
};
|
|
188
231
|
}
|
|
232
|
+
function excludeSource(source, exclusion, now) {
|
|
233
|
+
source.status = 'excluded';
|
|
234
|
+
source.exclusion = exclusion;
|
|
235
|
+
source.errorCode = null;
|
|
236
|
+
source.diagnostic = null;
|
|
237
|
+
source.detail = null;
|
|
238
|
+
source.sessionsAccepted = 0;
|
|
239
|
+
source.eventsAccepted = 0;
|
|
240
|
+
source.updatedAt = now.toISOString();
|
|
241
|
+
}
|
|
189
242
|
function persistProgress(repository, run, now) {
|
|
190
243
|
run.updatedAt = now.toISOString();
|
|
191
244
|
repository.saveBatchImportRun(run);
|
|
@@ -196,21 +249,21 @@ async function assertManifestSourceUnchanged(source) {
|
|
|
196
249
|
details = await stat(source.locator);
|
|
197
250
|
}
|
|
198
251
|
catch {
|
|
199
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'discovered source is no longer available; run discovery again');
|
|
252
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'discovered source is no longer available; run discovery again', schemaIssueDetail('locator'));
|
|
200
253
|
}
|
|
201
254
|
if (details.size !== source.sizeBytes || details.mtime.toISOString() !== source.modifiedAt) {
|
|
202
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'discovered source changed after manifest creation; run discovery again');
|
|
255
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'discovered source changed after manifest creation; run discovery again', schemaIssueDetail(details.size !== source.sizeBytes ? 'sizeBytes' : 'modifiedAt'));
|
|
203
256
|
}
|
|
204
257
|
const fingerprint = await fingerprintSourceFile({
|
|
205
258
|
selectedPath: source.locator,
|
|
206
259
|
allowedRoots: [source.authorizedRoot],
|
|
207
260
|
});
|
|
208
261
|
if (fingerprint.digest !== source.digest || fingerprint.size !== source.sizeBytes) {
|
|
209
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'discovered source content changed after manifest creation; run discovery again');
|
|
262
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'discovered source content changed after manifest creation; run discovery again', schemaIssueDetail('digest'));
|
|
210
263
|
}
|
|
211
264
|
}
|
|
212
|
-
function adapterFor(provider) {
|
|
213
|
-
return createSessionAdapterRegistry().get(provider);
|
|
265
|
+
function adapterFor(provider, limits) {
|
|
266
|
+
return createSessionAdapterRegistry(limits).get(provider);
|
|
214
267
|
}
|
|
215
268
|
async function sessionSource(selected, adapter) {
|
|
216
269
|
const probe = await adapter.inspect(selected);
|
|
@@ -264,4 +317,191 @@ function isAccepted(status) {
|
|
|
264
317
|
|| status === 'duplicate'
|
|
265
318
|
|| status === 'previously-committed';
|
|
266
319
|
}
|
|
320
|
+
/** Reuse source import and normalized repository gates, without copying FTS/analytics. */
|
|
321
|
+
async function rebuildFromCatalog(options) {
|
|
322
|
+
const previous = options.carryForward;
|
|
323
|
+
if (previous === options.repository) {
|
|
324
|
+
throw new SessionContractError('INVALID_ARGUMENT', 'carry-forward requires a separate destination catalog');
|
|
325
|
+
}
|
|
326
|
+
const workspaceId = options.manifest.workspaceId;
|
|
327
|
+
const exclusions = resolveImportExclusions(undefined, options.exclude);
|
|
328
|
+
const byProvider = {};
|
|
329
|
+
const counts = (provider) => byProvider[provider] ??= {
|
|
330
|
+
reimported: 0, carried: 0, droppedAsDuplicate: 0, excluded: 0,
|
|
331
|
+
};
|
|
332
|
+
const oldSessions = catalogSessions(previous, workspaceId);
|
|
333
|
+
const available = [];
|
|
334
|
+
const present = new Set();
|
|
335
|
+
const excludedSources = new Set();
|
|
336
|
+
for (const source of options.manifest.sources) {
|
|
337
|
+
if (matchImportExclusion(source, exclusions) ?? source.exclusion) {
|
|
338
|
+
excludedSources.add(source.sourceId);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
let details;
|
|
342
|
+
try {
|
|
343
|
+
details = await stat(source.locator);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
|
|
347
|
+
continue;
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
// Refresh the snapshot, but retain the explicitly saved authorization and source identity.
|
|
351
|
+
const fingerprint = await fingerprintSourceFile({ selectedPath: source.locator,
|
|
352
|
+
allowedRoots: [source.authorizedRoot] });
|
|
353
|
+
available.push({ ...source, sizeBytes: details.size, modifiedAt: details.mtime.toISOString(),
|
|
354
|
+
digest: fingerprint.digest });
|
|
355
|
+
present.add(source.sourceId);
|
|
356
|
+
}
|
|
357
|
+
const manifest = { ...options.manifest, sources: available,
|
|
358
|
+
manifestId: sha256(JSON.stringify(['carry-forward-v1', options.manifest.manifestId, available])) };
|
|
359
|
+
const stagingDirectory = await mkdtemp(join(tmpdir(), 'aiwg-session-rebuild-'));
|
|
360
|
+
let staged;
|
|
361
|
+
try {
|
|
362
|
+
staged = new SessionRepository(join(stagingDirectory, 'staging.sqlite'));
|
|
363
|
+
const receipt = await importDiscoveryManifest({ ...options, manifest, repository: staged,
|
|
364
|
+
carryForward: undefined, fault: undefined });
|
|
365
|
+
const identities = new Set();
|
|
366
|
+
const destinationCodex = new Map(catalogSessions(options.repository, workspaceId)
|
|
367
|
+
.filter(session => session.provider === 'codex').map(session => [session.nativeSessionId, session.sourceId]));
|
|
368
|
+
const transfer = (repository, sessions, category) => {
|
|
369
|
+
// A legacy Codex rollout may have split replayed parent events into phantom rows.
|
|
370
|
+
// Recover the first session_meta ID for the whole source before grouping events.
|
|
371
|
+
const sourceGroups = new Map();
|
|
372
|
+
for (const session of sessions) {
|
|
373
|
+
const group = sourceGroups.get(session.sourceId) ?? [];
|
|
374
|
+
group.push(session);
|
|
375
|
+
sourceGroups.set(session.sourceId, group);
|
|
376
|
+
}
|
|
377
|
+
for (const [sourceId, group] of sourceGroups) {
|
|
378
|
+
if (options.signal?.aborted)
|
|
379
|
+
throw new SessionContractError('IMPORT_INTERRUPTED', 'catalog rebuild interrupted');
|
|
380
|
+
const source = repository.getSource(sourceId, workspaceId);
|
|
381
|
+
if (!source)
|
|
382
|
+
throw new SessionContractError('IMPORT_CONFLICT', 'catalog session has no authorized source');
|
|
383
|
+
const allEvents = group.flatMap(session => repository.listEvents(session.sessionId, workspaceId))
|
|
384
|
+
.sort((a, b) => a.sequence - b.sequence || a.eventId.localeCompare(b.eventId));
|
|
385
|
+
const rollout = source.provider === 'codex' ? recoverCodexRolloutIdentity(allEvents) : undefined;
|
|
386
|
+
const childId = rollout?.nativeSessionId;
|
|
387
|
+
// Prefer the child's aggregate when the legacy source also has phantom parent aggregates.
|
|
388
|
+
group.sort((a, b) => Number(b.nativeSessionId === childId) - Number(a.nativeSessionId === childId));
|
|
389
|
+
const canonical = new Map();
|
|
390
|
+
for (const session of group) {
|
|
391
|
+
const nativeId = childId ?? session.nativeSessionId;
|
|
392
|
+
const locator = options.manifest.sources.find(item => item.sourceId === sourceId)?.locator
|
|
393
|
+
?? source.redactedLocator;
|
|
394
|
+
if (excludedSources.has(sourceId) || matchImportExclusion({ provider: session.provider,
|
|
395
|
+
locator, locatorClass: source.locatorClass, nativeSessionId: nativeId }, exclusions)) {
|
|
396
|
+
counts(session.provider).excluded++;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const key = session.provider === 'codex' ? `${session.provider}\0${nativeId}` : session.sessionId;
|
|
400
|
+
if (identities.has(key)) {
|
|
401
|
+
counts(session.provider).droppedAsDuplicate++;
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const existing = canonical.get(key);
|
|
405
|
+
if (existing) {
|
|
406
|
+
counts(session.provider).droppedAsDuplicate++;
|
|
407
|
+
existing.events.push(...repository.listEvents(session.sessionId, workspaceId));
|
|
408
|
+
}
|
|
409
|
+
else
|
|
410
|
+
canonical.set(key, { session: { ...session, nativeSessionId: nativeId },
|
|
411
|
+
events: repository.listEvents(session.sessionId, workspaceId) });
|
|
412
|
+
}
|
|
413
|
+
for (const [key, entry] of canonical) {
|
|
414
|
+
if (source.provider === 'codex' && destinationCodex.has(entry.session.nativeSessionId)
|
|
415
|
+
&& destinationCodex.get(entry.session.nativeSessionId) !== sourceId) {
|
|
416
|
+
throw new SessionContractError('IMPORT_CONFLICT', 'destination has a different source for this Codex identity; select a fresh catalog');
|
|
417
|
+
}
|
|
418
|
+
const session = redactSessionValue({ ...entry.session,
|
|
419
|
+
sessionId: stableSessionId(source.provider, sourceId, entry.session.nativeSessionId),
|
|
420
|
+
}).value;
|
|
421
|
+
// Hash only sanitized content; receipts and IDs must never retain old plaintext.
|
|
422
|
+
const sanitized = entry.events.map(sanitizeSessionEvent);
|
|
423
|
+
const runId = sha256(JSON.stringify(['catalog-carry-v1', session.sessionId, sanitized]));
|
|
424
|
+
const events = sanitized.map(event => {
|
|
425
|
+
const digest = event.digest;
|
|
426
|
+
return { ...event, sessionId: session.sessionId, importRunId: runId, digest,
|
|
427
|
+
eventId: stableEventId(source.provider, sourceId, {
|
|
428
|
+
nativeSessionId: session.nativeSessionId, nativeEventId: event.nativeId ?? event.eventId,
|
|
429
|
+
sequence: event.sequence, kind: event.kind, text: event.searchableText,
|
|
430
|
+
rawReference: event.rawReference,
|
|
431
|
+
}, digest) };
|
|
432
|
+
});
|
|
433
|
+
if (rollout && Object.keys(rollout.lineage).length > 0) {
|
|
434
|
+
session.extensions['native.codex'] = { lineage: rollout.lineage };
|
|
435
|
+
}
|
|
436
|
+
session.intent = deriveSessionIntent(events);
|
|
437
|
+
const checkpoint = repository.getCheckpoint(sourceId, source.adapterVersion)
|
|
438
|
+
?? { cursor: '', recordsRead: events.length, bytesRead: Buffer.byteLength(JSON.stringify(events)) };
|
|
439
|
+
options.repository.applyImport({ source, sessions: [session], events,
|
|
440
|
+
run: { contractVersion: SESSION_CONTRACT_VERSION, importRunId: runId, sourceId,
|
|
441
|
+
parserVersion: source.adapterVersion, policyVersion: '1.0.0',
|
|
442
|
+
sourceSchemaVersion: source.sourceSchemaVersion, consistency: source.consistency,
|
|
443
|
+
status: 'running', checkpoint, startedAt: (options.now?.() ?? new Date()).toISOString(),
|
|
444
|
+
completedAt: null, errorCode: null } }, checkpoint);
|
|
445
|
+
for (const old of group.filter(item => (childId ?? item.nativeSessionId) === entry.session.nativeSessionId)) {
|
|
446
|
+
for (const tag of repository.listTags(old.sessionId, workspaceId)) {
|
|
447
|
+
options.repository.tagSession(session.sessionId, tag, workspaceId);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
identities.add(key);
|
|
451
|
+
counts(source.provider)[category]++;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
const reimported = catalogSessions(staged, workspaceId);
|
|
456
|
+
transfer(staged, reimported, 'reimported');
|
|
457
|
+
for (const sourceId of present) {
|
|
458
|
+
const old = oldSessions.filter(session => session.sourceId === sourceId && session.provider === 'codex');
|
|
459
|
+
const imported = reimported.filter(session => session.sourceId === sourceId);
|
|
460
|
+
// Account for phantom rows eliminated by current source parsing, separately from copied duplicates.
|
|
461
|
+
if (imported.length > 0 && old.length > imported.length) {
|
|
462
|
+
counts('codex').droppedAsDuplicate += old.length - imported.length;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
transfer(previous, oldSessions.filter(session => !present.has(session.sourceId)), 'carried');
|
|
466
|
+
options.repository.reindex(workspaceId);
|
|
467
|
+
const finalSessions = catalogSessions(options.repository, workspaceId);
|
|
468
|
+
const sourceIds = new Set([...receipt.run.sources.map(item => item.sourceId),
|
|
469
|
+
...oldSessions.map(session => session.sourceId)]);
|
|
470
|
+
for (const sourceId of sourceIds) {
|
|
471
|
+
const accepted = finalSessions.filter(session => session.sourceId === sourceId);
|
|
472
|
+
let disposition = receipt.run.sources.find(item => item.sourceId === sourceId);
|
|
473
|
+
if (!disposition) {
|
|
474
|
+
const old = oldSessions.find(session => session.sourceId === sourceId);
|
|
475
|
+
const excluded = excludedSources.has(sourceId) || matchImportExclusion({
|
|
476
|
+
provider: old.provider, nativeSessionId: old.nativeSessionId,
|
|
477
|
+
locator: previous.getSource(sourceId, workspaceId)?.redactedLocator ?? '', locatorClass: 'catalog',
|
|
478
|
+
}, exclusions);
|
|
479
|
+
disposition = { sourceId, provider: old.provider,
|
|
480
|
+
status: excluded ? 'excluded' : accepted.length > 0 ? 'committed' : 'duplicate',
|
|
481
|
+
attempts: 1, sessionsAccepted: 0, eventsAccepted: 0,
|
|
482
|
+
errorCode: null, diagnostic: null, updatedAt: receipt.run.updatedAt };
|
|
483
|
+
receipt.run.sources.push(disposition);
|
|
484
|
+
}
|
|
485
|
+
disposition.sessionsAccepted = accepted.length;
|
|
486
|
+
disposition.eventsAccepted = accepted.reduce((sum, session) => sum + options.repository.listEvents(session.sessionId, workspaceId).length, 0);
|
|
487
|
+
}
|
|
488
|
+
receipt.run.carryForward = { byProvider };
|
|
489
|
+
options.repository.saveBatchImportRun(receipt.run);
|
|
490
|
+
return redactSessionValue(receiptFor(receipt.run, options.now?.() ?? new Date())).value;
|
|
491
|
+
}
|
|
492
|
+
finally {
|
|
493
|
+
staged?.close();
|
|
494
|
+
await rm(stagingDirectory, { recursive: true, force: true });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function catalogSessions(repository, workspaceId) {
|
|
498
|
+
const sessions = [];
|
|
499
|
+
let cursor;
|
|
500
|
+
do {
|
|
501
|
+
const page = repository.listSessions({ workspaceId, limit: 500, cursor });
|
|
502
|
+
sessions.push(...page.items);
|
|
503
|
+
cursor = page.nextCursor ?? undefined;
|
|
504
|
+
} while (cursor);
|
|
505
|
+
return sessions;
|
|
506
|
+
}
|
|
267
507
|
//# sourceMappingURL=batch-import.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { redactSessionText, redactSessionValue } from './policy.js';
|
|
3
4
|
export const SESSION_CONTRACT_VERSION = '1.0.0';
|
|
4
5
|
export const SESSION_PROVIDER_IDS = [
|
|
5
6
|
'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
|
|
@@ -19,9 +20,18 @@ export const SessionRuntimeIdentitySchema = z.object({
|
|
|
19
20
|
canonicalRoot: z.string().nullable(),
|
|
20
21
|
diagnostics: z.array(z.string()),
|
|
21
22
|
});
|
|
23
|
+
export const SessionProviderScopeWarningSchema = z.object({
|
|
24
|
+
code: z.literal('CONFIGURED_PROVIDERS_OUTSIDE_SCOPE'),
|
|
25
|
+
configuredProviders: z.array(ExpectedSessionProviderSchema),
|
|
26
|
+
expectedProviders: z.array(ExpectedSessionProviderSchema),
|
|
27
|
+
missingProviders: z.array(ExpectedSessionProviderSchema),
|
|
28
|
+
message: z.string(),
|
|
29
|
+
});
|
|
22
30
|
export const SessionCoveragePreflightSchema = z.object({
|
|
23
31
|
expectedProviders: z.array(ExpectedSessionProviderSchema).min(1),
|
|
24
|
-
expectedProviderBasis: z.enum(['explicit', 'declared-default', 'legacy']),
|
|
32
|
+
expectedProviderBasis: z.enum(['explicit', 'workspace-config', 'declared-default', 'legacy']),
|
|
33
|
+
configuredProviders: z.array(ExpectedSessionProviderSchema).optional(),
|
|
34
|
+
scopeWarnings: z.array(SessionProviderScopeWarningSchema).optional(),
|
|
25
35
|
supportedProviders: z.array(ExpectedSessionProviderSchema),
|
|
26
36
|
runtime: SessionRuntimeIdentitySchema,
|
|
27
37
|
status: z.enum(['partial', 'complete']),
|
|
@@ -362,11 +372,48 @@ export function sha256(value) {
|
|
|
362
372
|
function stableId(prefix, ...parts) {
|
|
363
373
|
return `${prefix}_${createHash('sha256').update(parts.join('\0')).digest('hex')}`;
|
|
364
374
|
}
|
|
375
|
+
export const SessionRejectionDetailSchema = z.union([
|
|
376
|
+
z.object({ limit: z.string(), configured: z.number(), observed: z.number(),
|
|
377
|
+
setting: z.string().optional(), origin: z.enum(['flag', 'env', 'config', 'default']).optional() }),
|
|
378
|
+
z.object({ issues: z.array(z.object({ path: z.string(), code: z.string() })) }),
|
|
379
|
+
]);
|
|
380
|
+
export function schemaIssueDetail(path, code = 'custom') {
|
|
381
|
+
return { issues: [{ path, code }] };
|
|
382
|
+
}
|
|
383
|
+
export function assertSessionResourceLimit(limit, configured, observed) {
|
|
384
|
+
if (observed > configured) {
|
|
385
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session resource limit exceeded', {
|
|
386
|
+
limit, configured, observed,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/** Extract schema locations and validator codes without messages or input values. */
|
|
391
|
+
export function schemaRejectionDetail(error) {
|
|
392
|
+
const issues = [];
|
|
393
|
+
const visit = (entries) => {
|
|
394
|
+
for (const issue of entries) {
|
|
395
|
+
if (issues.length >= 20)
|
|
396
|
+
return;
|
|
397
|
+
if (issue.code === 'invalid_union') {
|
|
398
|
+
for (const branch of issue.unionErrors)
|
|
399
|
+
visit(branch.issues);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
issues.push({ path: issue.path.join('.') || '<root>', code: issue.code });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
visit(error.issues);
|
|
407
|
+
return redactSessionValue({ issues }).value;
|
|
408
|
+
}
|
|
365
409
|
export class SessionContractError extends Error {
|
|
366
410
|
code;
|
|
367
|
-
|
|
368
|
-
|
|
411
|
+
detail;
|
|
412
|
+
constructor(code, message, detail) {
|
|
413
|
+
super(redactSessionText(message).text);
|
|
369
414
|
this.code = code;
|
|
415
|
+
this.detail = detail;
|
|
416
|
+
this.detail = redactSessionValue(detail).value;
|
|
370
417
|
this.name = 'SessionContractError';
|
|
371
418
|
}
|
|
372
419
|
}
|