@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,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
-
import {
|
|
2
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
+
import { streamBoundedJsonLines, } from '../readers.js';
|
|
4
4
|
export const OPENCLAW_ADAPTER_VERSION = '1.0.0';
|
|
5
5
|
export const OPENCLAW_SOURCE_SCHEMA_VERSION = '1.0.0';
|
|
6
6
|
export const OPENCLAW_NATIVE_SCHEMA_VERSION = '16.0.0';
|
|
@@ -80,6 +80,13 @@ export class OpenClawSessionAdapter {
|
|
|
80
80
|
// Gateway and consistent snapshots require explicit operator selection.
|
|
81
81
|
}
|
|
82
82
|
async inspect(source) {
|
|
83
|
+
if (SOURCE_CLASSES.has(source.locatorClass)) {
|
|
84
|
+
const records = this.readLocalSource(source, true);
|
|
85
|
+
let result = await records.next();
|
|
86
|
+
while (!result.done)
|
|
87
|
+
result = await records.next();
|
|
88
|
+
return result.value;
|
|
89
|
+
}
|
|
83
90
|
const parsed = await this.readSource(source, true);
|
|
84
91
|
return {
|
|
85
92
|
sourceSchemaVersion: parsed.schemaVersion,
|
|
@@ -89,57 +96,7 @@ export class OpenClawSessionAdapter {
|
|
|
89
96
|
}
|
|
90
97
|
async *stream(source, cursor) {
|
|
91
98
|
if (SOURCE_CLASSES.has(source.locatorClass)) {
|
|
92
|
-
|
|
93
|
-
selectedPath: source.locator,
|
|
94
|
-
allowedRoots: source.authorizedScope.allowedRoots,
|
|
95
|
-
}, {
|
|
96
|
-
consistency: source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
97
|
-
? 'complete' : 'provisional',
|
|
98
|
-
limits: this.limits,
|
|
99
|
-
});
|
|
100
|
-
const start = parseCursor(cursor?.value);
|
|
101
|
-
let outputIndex = 0;
|
|
102
|
-
let nativeSchema = null;
|
|
103
|
-
let eventSchema = null;
|
|
104
|
-
let sawRecord = false;
|
|
105
|
-
for await (const line of input) {
|
|
106
|
-
sawRecord = true;
|
|
107
|
-
const parsed = SnapshotSchema.safeParse(line.value);
|
|
108
|
-
if (!parsed.success) {
|
|
109
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw snapshot is malformed');
|
|
110
|
-
}
|
|
111
|
-
const snapshot = parsed.data;
|
|
112
|
-
const currentNative = nativeVersion(snapshot.schemaVersion);
|
|
113
|
-
const currentEvent = nativeVersion(snapshot.eventVersion);
|
|
114
|
-
if ((nativeSchema && nativeSchema !== currentNative)
|
|
115
|
-
|| (eventSchema && eventSchema !== currentEvent)) {
|
|
116
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenClaw snapshot schemas');
|
|
117
|
-
}
|
|
118
|
-
nativeSchema = currentNative;
|
|
119
|
-
eventSchema = currentEvent;
|
|
120
|
-
assertSupportedSchemaMajor(nativeSchema, 16);
|
|
121
|
-
assertSupportedSchemaMajor(eventSchema, 3);
|
|
122
|
-
if (source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
123
|
-
&& snapshot.snapshotConsistency !== 'sqlite-backup') {
|
|
124
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'OpenClaw snapshot lacks sqlite3.backup() consistency evidence');
|
|
125
|
-
}
|
|
126
|
-
if (snapshot.incognito) {
|
|
127
|
-
throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'OpenClaw incognito session capture is unavailable by provider policy');
|
|
128
|
-
}
|
|
129
|
-
if (snapshot.gateway?.mode === 'remote'
|
|
130
|
-
&& snapshot.gateway.hostId !== snapshot.gateway.expectedHostId) {
|
|
131
|
-
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'OpenClaw remote Gateway identity does not match the authorized host');
|
|
132
|
-
}
|
|
133
|
-
for (const record of normalizeSnapshot(snapshot, line, source.locatorClass)) {
|
|
134
|
-
if (outputIndex++ >= start)
|
|
135
|
-
yield record;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (!sawRecord)
|
|
139
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw source is empty');
|
|
140
|
-
if (input.incompleteTail && source.locatorClass === 'openclaw-consistent-snapshot-jsonl') {
|
|
141
|
-
throw new SessionContractError('TRUNCATED_SOURCE', 'OpenClaw consistent snapshot is truncated');
|
|
142
|
-
}
|
|
99
|
+
yield* this.readLocalSource(source, false, cursor);
|
|
143
100
|
return;
|
|
144
101
|
}
|
|
145
102
|
const parsed = await this.readSource(source, false);
|
|
@@ -147,6 +104,79 @@ export class OpenClawSessionAdapter {
|
|
|
147
104
|
for (const record of parsed.records.slice(start))
|
|
148
105
|
yield record;
|
|
149
106
|
}
|
|
107
|
+
async *readLocalSource(source, inspection, cursor) {
|
|
108
|
+
const input = await streamBoundedJsonLines({
|
|
109
|
+
selectedPath: source.locator,
|
|
110
|
+
allowedRoots: source.authorizedScope.allowedRoots,
|
|
111
|
+
}, {
|
|
112
|
+
consistency: source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
113
|
+
? 'complete' : 'provisional',
|
|
114
|
+
limits: this.limits,
|
|
115
|
+
});
|
|
116
|
+
const start = parseCursor(cursor?.value);
|
|
117
|
+
let outputIndex = 0;
|
|
118
|
+
let nativeSchema = null;
|
|
119
|
+
let eventSchema = null;
|
|
120
|
+
let sawRecord = false;
|
|
121
|
+
let complete = true;
|
|
122
|
+
let incognito = false;
|
|
123
|
+
let remoteMismatch = false;
|
|
124
|
+
let projectionIncomplete = false;
|
|
125
|
+
for await (const line of input) {
|
|
126
|
+
sawRecord = true;
|
|
127
|
+
const parsed = SnapshotSchema.safeParse(line.value);
|
|
128
|
+
if (!parsed.success) {
|
|
129
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw snapshot is malformed', schemaRejectionDetail(parsed.error));
|
|
130
|
+
}
|
|
131
|
+
const snapshot = parsed.data;
|
|
132
|
+
const currentNative = nativeVersion(snapshot.schemaVersion);
|
|
133
|
+
const currentEvent = nativeVersion(snapshot.eventVersion);
|
|
134
|
+
if ((nativeSchema && nativeSchema !== currentNative)
|
|
135
|
+
|| (eventSchema && eventSchema !== currentEvent)) {
|
|
136
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenClaw snapshot schemas', { issues: [{ path: nativeSchema && nativeSchema !== currentNative ? 'schemaVersion' : 'eventVersion', code: 'custom' }] });
|
|
137
|
+
}
|
|
138
|
+
nativeSchema = currentNative;
|
|
139
|
+
eventSchema = currentEvent;
|
|
140
|
+
assertSupportedSchemaMajor(nativeSchema, 16);
|
|
141
|
+
assertSupportedSchemaMajor(eventSchema, 3);
|
|
142
|
+
if (source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
143
|
+
&& snapshot.snapshotConsistency !== 'sqlite-backup') {
|
|
144
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'OpenClaw snapshot lacks sqlite3.backup() consistency evidence', schemaIssueDetail('snapshotConsistency'));
|
|
145
|
+
}
|
|
146
|
+
incognito ||= snapshot.incognito === true;
|
|
147
|
+
const mismatched = snapshot.gateway?.mode === 'remote'
|
|
148
|
+
&& snapshot.gateway.hostId !== snapshot.gateway.expectedHostId;
|
|
149
|
+
remoteMismatch ||= mismatched;
|
|
150
|
+
projectionIncomplete ||= Boolean(snapshot.projection
|
|
151
|
+
&& (snapshot.projection.kind !== 'canonical' || !snapshot.projection.complete));
|
|
152
|
+
complete &&= snapshot.sessions.every(sessionComplete);
|
|
153
|
+
if (!inspection && snapshot.incognito) {
|
|
154
|
+
throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'OpenClaw incognito session capture is unavailable by provider policy');
|
|
155
|
+
}
|
|
156
|
+
if (!inspection && mismatched) {
|
|
157
|
+
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'OpenClaw remote Gateway identity does not match the authorized host');
|
|
158
|
+
}
|
|
159
|
+
if (snapshot.incognito || mismatched)
|
|
160
|
+
continue;
|
|
161
|
+
for (const record of normalizeSnapshot(snapshot, line, source.locatorClass)) {
|
|
162
|
+
if (outputIndex++ >= start)
|
|
163
|
+
yield record;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!sawRecord)
|
|
167
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw source is empty', schemaIssueDetail('<root>'));
|
|
168
|
+
if (input.incompleteTail && source.locatorClass === 'openclaw-consistent-snapshot-jsonl') {
|
|
169
|
+
throw new SessionContractError('TRUNCATED_SOURCE', 'OpenClaw consistent snapshot is truncated');
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
sourceSchemaVersion: OPENCLAW_SOURCE_SCHEMA_VERSION,
|
|
173
|
+
consistency: source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
174
|
+
? 'consistent-snapshot'
|
|
175
|
+
: complete && !projectionIncomplete ? 'complete' : 'provisional',
|
|
176
|
+
operationalState: incognito ? 'inaccessible'
|
|
177
|
+
: remoteMismatch || projectionIncomplete ? 'degraded' : 'available',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
150
180
|
async readSource(source, inspection) {
|
|
151
181
|
if (source.locatorClass === 'openclaw-state-db') {
|
|
152
182
|
throw new SessionContractError('UNSUPPORTED_OPERATION', 'raw OpenClaw SQLite access is unsafe; use Gateway acquisition or a verified sqlite3.backup() projection');
|
|
@@ -161,42 +191,24 @@ export class OpenClawSessionAdapter {
|
|
|
161
191
|
}
|
|
162
192
|
input = (await this.gateway.snapshot(source)).map(toRecord);
|
|
163
193
|
}
|
|
164
|
-
else if (SOURCE_CLASSES.has(source.locatorClass)) {
|
|
165
|
-
const result = await readBoundedJsonLines({
|
|
166
|
-
selectedPath: source.locator,
|
|
167
|
-
allowedRoots: source.authorizedScope.allowedRoots,
|
|
168
|
-
}, {
|
|
169
|
-
consistency: source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
170
|
-
? 'complete' : 'provisional',
|
|
171
|
-
limits: this.limits,
|
|
172
|
-
});
|
|
173
|
-
if (result.incompleteTail && source.locatorClass === 'openclaw-consistent-snapshot-jsonl') {
|
|
174
|
-
throw new SessionContractError('TRUNCATED_SOURCE', 'OpenClaw consistent snapshot is truncated');
|
|
175
|
-
}
|
|
176
|
-
input = result.records;
|
|
177
|
-
}
|
|
178
194
|
else {
|
|
179
195
|
throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported OpenClaw source class');
|
|
180
196
|
}
|
|
181
197
|
if (input.length === 0)
|
|
182
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw source is empty');
|
|
198
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw source is empty', schemaIssueDetail('<root>'));
|
|
183
199
|
const parsed = input.map(({ value, ...line }) => {
|
|
184
200
|
const result = SnapshotSchema.safeParse(value);
|
|
185
201
|
if (!result.success)
|
|
186
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw snapshot is malformed');
|
|
202
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenClaw snapshot is malformed', schemaRejectionDetail(result.error));
|
|
187
203
|
return { value: result.data, ...line };
|
|
188
204
|
});
|
|
189
205
|
const nativeSchemas = new Set(parsed.map(({ value }) => nativeVersion(value.schemaVersion)));
|
|
190
206
|
const eventSchemas = new Set(parsed.map(({ value }) => nativeVersion(value.eventVersion)));
|
|
191
207
|
if (nativeSchemas.size !== 1 || eventSchemas.size !== 1) {
|
|
192
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenClaw snapshot schemas');
|
|
208
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenClaw snapshot schemas', { issues: [{ path: nativeSchemas.size !== 1 ? 'schemaVersion' : 'eventVersion', code: 'custom' }] });
|
|
193
209
|
}
|
|
194
210
|
assertSupportedSchemaMajor([...nativeSchemas][0], 16);
|
|
195
211
|
assertSupportedSchemaMajor([...eventSchemas][0], 3);
|
|
196
|
-
if (source.locatorClass === 'openclaw-consistent-snapshot-jsonl'
|
|
197
|
-
&& parsed.some(({ value }) => value.snapshotConsistency !== 'sqlite-backup')) {
|
|
198
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'OpenClaw snapshot lacks sqlite3.backup() consistency evidence');
|
|
199
|
-
}
|
|
200
212
|
const incognito = parsed.some(({ value }) => value.incognito);
|
|
201
213
|
const remoteMismatch = parsed.some(({ value }) => value.gateway?.mode === 'remote'
|
|
202
214
|
&& value.gateway.hostId !== value.gateway.expectedHostId);
|
|
@@ -209,10 +221,8 @@ export class OpenClawSessionAdapter {
|
|
|
209
221
|
const projectionIncomplete = parsed.some(({ value }) => value.projection && (value.projection.kind !== 'canonical' || !value.projection.complete));
|
|
210
222
|
return {
|
|
211
223
|
schemaVersion: OPENCLAW_SOURCE_SCHEMA_VERSION,
|
|
212
|
-
consistency:
|
|
213
|
-
? '
|
|
214
|
-
: parsed.every(({ value }) => value.sessions.every(sessionComplete)) && !projectionIncomplete
|
|
215
|
-
? 'complete' : 'provisional',
|
|
224
|
+
consistency: parsed.every(({ value }) => value.sessions.every(sessionComplete)) && !projectionIncomplete
|
|
225
|
+
? 'complete' : 'provisional',
|
|
216
226
|
operationalState: incognito ? 'inaccessible' : remoteMismatch || projectionIncomplete ? 'degraded' : 'available',
|
|
217
227
|
records: incognito || remoteMismatch
|
|
218
228
|
? []
|
|
@@ -354,7 +364,7 @@ function parseCursor(value) {
|
|
|
354
364
|
if (!value)
|
|
355
365
|
return 0;
|
|
356
366
|
if (!/^\d+$/.test(value))
|
|
357
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OpenClaw cursor');
|
|
367
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OpenClaw cursor', schemaIssueDetail('cursor'));
|
|
358
368
|
return Number(value);
|
|
359
369
|
}
|
|
360
370
|
function toRecord(value, sequence) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
-
import { readBoundedJson,
|
|
2
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
+
import { readBoundedJson, streamBoundedJsonLines, } from '../readers.js';
|
|
4
4
|
export const OPENCODE_ADAPTER_VERSION = '1.0.0';
|
|
5
5
|
export const OPENCODE_EXPORT_SCHEMA_VERSION = '1.0.0';
|
|
6
6
|
const SessionInfoSchema = z.object({
|
|
@@ -82,6 +82,14 @@ export class OpenCodeSessionAdapter {
|
|
|
82
82
|
// Exports and loopback API/SSE endpoints require explicit selection.
|
|
83
83
|
}
|
|
84
84
|
async inspect(source) {
|
|
85
|
+
if (source.locatorClass === 'opencode-sse-jsonl') {
|
|
86
|
+
const records = this.stream(source);
|
|
87
|
+
let result = await records.next();
|
|
88
|
+
while (!result.done)
|
|
89
|
+
result = await records.next();
|
|
90
|
+
if (result.value)
|
|
91
|
+
return result.value;
|
|
92
|
+
}
|
|
85
93
|
const parsed = await this.readSource(source);
|
|
86
94
|
return {
|
|
87
95
|
sourceSchemaVersion: parsed.schemaVersion,
|
|
@@ -105,12 +113,12 @@ export class OpenCodeSessionAdapter {
|
|
|
105
113
|
for await (const line of input) {
|
|
106
114
|
const parsed = EventSchema.safeParse(line.value);
|
|
107
115
|
if (!parsed.success) {
|
|
108
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE event is malformed');
|
|
116
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE event is malformed', schemaRejectionDetail(parsed.error));
|
|
109
117
|
}
|
|
110
118
|
const event = parsed.data;
|
|
111
119
|
const currentSchema = event.schemaVersion ?? OPENCODE_EXPORT_SCHEMA_VERSION;
|
|
112
120
|
if (schemaVersion && schemaVersion !== currentSchema) {
|
|
113
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenCode event schemas');
|
|
121
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenCode event schemas', schemaIssueDetail('schemaVersion'));
|
|
114
122
|
}
|
|
115
123
|
schemaVersion = currentSchema;
|
|
116
124
|
assertSupportedSchemaMajor(schemaVersion);
|
|
@@ -135,7 +143,7 @@ export class OpenCodeSessionAdapter {
|
|
|
135
143
|
messages.set(candidate.data.id, candidate.data);
|
|
136
144
|
const session = sessions.get(candidate.data.sessionID);
|
|
137
145
|
if (!session) {
|
|
138
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode message event precedes its session identity');
|
|
146
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode message event precedes its session identity', schemaIssueDetail('properties.info.sessionID'));
|
|
139
147
|
}
|
|
140
148
|
const common = sessionExtensions({
|
|
141
149
|
info: session, messages: [], sanitized: event.sanitized !== false,
|
|
@@ -150,7 +158,7 @@ export class OpenCodeSessionAdapter {
|
|
|
150
158
|
const message = messages.get(candidate.data.messageID);
|
|
151
159
|
const session = sessions.get(candidate.data.sessionID);
|
|
152
160
|
if (!message || !session) {
|
|
153
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode part event precedes its message or session identity');
|
|
161
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode part event precedes its message or session identity', schemaIssueDetail(!message ? 'properties.part.messageID' : 'properties.part.sessionID'));
|
|
154
162
|
}
|
|
155
163
|
const common = sessionExtensions({
|
|
156
164
|
info: session, messages: [], sanitized: event.sanitized !== false,
|
|
@@ -161,7 +169,7 @@ export class OpenCodeSessionAdapter {
|
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
171
|
if (sessions.size + messages.size > maxJoinStates) {
|
|
164
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OpenCode SSE join state exceeds the bounded streaming limit');
|
|
172
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OpenCode SSE join state exceeds the bounded streaming limit', { limit: 'maxJoinStates', configured: maxJoinStates, observed: sessions.size + messages.size });
|
|
165
173
|
}
|
|
166
174
|
for (const record of emitted) {
|
|
167
175
|
if (outputIndex++ >= start)
|
|
@@ -169,14 +177,19 @@ export class OpenCodeSessionAdapter {
|
|
|
169
177
|
}
|
|
170
178
|
}
|
|
171
179
|
if (!sawSession) {
|
|
172
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE stream lacks a session identity');
|
|
180
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE stream lacks a session identity', schemaIssueDetail('properties.info.id'));
|
|
173
181
|
}
|
|
174
|
-
return
|
|
182
|
+
return {
|
|
183
|
+
sourceSchemaVersion: schemaVersion ?? OPENCODE_EXPORT_SCHEMA_VERSION,
|
|
184
|
+
consistency: 'provisional',
|
|
185
|
+
operationalState: 'available',
|
|
186
|
+
};
|
|
175
187
|
}
|
|
176
188
|
const parsed = await this.readSource(source);
|
|
177
189
|
const start = parseCursor(cursor?.value);
|
|
178
190
|
for (const record of parsed.records.slice(start))
|
|
179
191
|
yield record;
|
|
192
|
+
return undefined;
|
|
180
193
|
}
|
|
181
194
|
async readSource(source) {
|
|
182
195
|
if (source.locatorClass === 'opencode-sqlite') {
|
|
@@ -189,16 +202,6 @@ export class OpenCodeSessionAdapter {
|
|
|
189
202
|
}, this.limits);
|
|
190
203
|
return normalizeExport(value, source.locatorClass);
|
|
191
204
|
}
|
|
192
|
-
if (source.locatorClass === 'opencode-sse-jsonl') {
|
|
193
|
-
const result = await readBoundedJsonLines({
|
|
194
|
-
selectedPath: source.locator,
|
|
195
|
-
allowedRoots: source.authorizedScope.allowedRoots,
|
|
196
|
-
}, { consistency: 'provisional', limits: this.limits });
|
|
197
|
-
if (result.records.length === 0) {
|
|
198
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE source is empty');
|
|
199
|
-
}
|
|
200
|
-
return normalizeEvents(result.records, source.locatorClass);
|
|
201
|
-
}
|
|
202
205
|
const kind = source.locatorClass === 'opencode-local-api'
|
|
203
206
|
? 'api'
|
|
204
207
|
: source.locatorClass === 'opencode-live-sse' ? 'sse' : undefined;
|
|
@@ -222,7 +225,7 @@ export class OpenCodeSessionAdapter {
|
|
|
222
225
|
function normalizeExport(input, locatorClass, provisional = false) {
|
|
223
226
|
const parsed = ExportSchema.safeParse(input);
|
|
224
227
|
if (!parsed.success)
|
|
225
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode export is malformed');
|
|
228
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode export is malformed', schemaRejectionDetail(parsed.error));
|
|
226
229
|
const value = parsed.data;
|
|
227
230
|
const schema = value.schemaVersion ?? OPENCODE_EXPORT_SCHEMA_VERSION;
|
|
228
231
|
assertSupportedSchemaMajor(schema);
|
|
@@ -251,12 +254,12 @@ function normalizeEvents(input, locatorClass) {
|
|
|
251
254
|
const parsed = input.map(({ value, ...line }) => {
|
|
252
255
|
const result = EventSchema.safeParse(value);
|
|
253
256
|
if (!result.success)
|
|
254
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE event is malformed');
|
|
257
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE event is malformed', schemaRejectionDetail(result.error));
|
|
255
258
|
return { value: result.data, ...line };
|
|
256
259
|
});
|
|
257
260
|
const versions = new Set(parsed.map((item) => item.value.schemaVersion ?? OPENCODE_EXPORT_SCHEMA_VERSION));
|
|
258
261
|
if (versions.size !== 1)
|
|
259
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenCode event schemas');
|
|
262
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenCode event schemas', schemaIssueDetail('schemaVersion'));
|
|
260
263
|
assertSupportedSchemaMajor([...versions][0]);
|
|
261
264
|
const sessions = new Map();
|
|
262
265
|
const messages = new Map();
|
|
@@ -280,7 +283,7 @@ function normalizeEvents(input, locatorClass) {
|
|
|
280
283
|
}
|
|
281
284
|
}
|
|
282
285
|
if (sessions.size === 0) {
|
|
283
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE stream lacks a session identity');
|
|
286
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenCode SSE stream lacks a session identity', schemaIssueDetail('properties.info.id'));
|
|
284
287
|
}
|
|
285
288
|
const records = [];
|
|
286
289
|
for (const session of sessions.values()) {
|
|
@@ -444,7 +447,7 @@ function parseCursor(value) {
|
|
|
444
447
|
if (!value)
|
|
445
448
|
return 0;
|
|
446
449
|
if (!/^\d+$/.test(value))
|
|
447
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OpenCode cursor');
|
|
450
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OpenCode cursor', schemaIssueDetail('cursor'));
|
|
448
451
|
return Number(value);
|
|
449
452
|
}
|
|
450
453
|
const SESSION_INFO_KEYS = new Set([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
-
import {
|
|
2
|
+
import { schemaRejectionDetail, schemaIssueDetail, SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
+
import { streamBoundedJsonLines, } from '../readers.js';
|
|
4
4
|
export const OPENHUMAN_ADAPTER_VERSION = '1.0.0';
|
|
5
5
|
export const OPENHUMAN_SOURCE_SCHEMA_VERSION = '1.0.0';
|
|
6
6
|
const MetadataSchema = z.object({
|
|
@@ -76,12 +76,11 @@ export class OpenHumanSessionAdapter {
|
|
|
76
76
|
// session_raw and enrichment bundles require explicit selection.
|
|
77
77
|
}
|
|
78
78
|
async inspect(source) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
};
|
|
79
|
+
const records = this.stream(source);
|
|
80
|
+
let result = await records.next();
|
|
81
|
+
while (!result.done)
|
|
82
|
+
result = await records.next();
|
|
83
|
+
return result.value;
|
|
85
84
|
}
|
|
86
85
|
async *stream(source, cursor) {
|
|
87
86
|
if (source.locatorClass !== 'openhuman-session-raw-jsonl'
|
|
@@ -95,6 +94,7 @@ export class OpenHumanSessionAdapter {
|
|
|
95
94
|
}, { consistency: 'provisional', limits: this.limits });
|
|
96
95
|
const threads = new Map();
|
|
97
96
|
const turns = new Map();
|
|
97
|
+
const sessions = new Map();
|
|
98
98
|
const maxJoinStates = Math.min(this.limits?.maxRecords ?? 1_000_000, 10_000);
|
|
99
99
|
let schemaVersion = null;
|
|
100
100
|
let outputIndex = 0;
|
|
@@ -102,12 +102,12 @@ export class OpenHumanSessionAdapter {
|
|
|
102
102
|
for await (const line of input) {
|
|
103
103
|
const parsed = RecordSchema.safeParse(line.value);
|
|
104
104
|
if (!parsed.success) {
|
|
105
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman transcript record is malformed');
|
|
105
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman transcript record is malformed', schemaRejectionDetail(parsed.error));
|
|
106
106
|
}
|
|
107
107
|
const value = parsed.data;
|
|
108
108
|
const currentVersion = version(value.schemaVersion);
|
|
109
109
|
if (schemaVersion && schemaVersion !== currentVersion) {
|
|
110
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenHuman schema versions');
|
|
110
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenHuman schema versions', schemaIssueDetail('schemaVersion'));
|
|
111
111
|
}
|
|
112
112
|
schemaVersion = currentVersion;
|
|
113
113
|
assertSupportedSchemaMajor(schemaVersion);
|
|
@@ -119,64 +119,22 @@ export class OpenHumanSessionAdapter {
|
|
|
119
119
|
}
|
|
120
120
|
else {
|
|
121
121
|
sawRaw = true;
|
|
122
|
+
sessions.set(value.session_id, value.active === false || value.type === 'session_end');
|
|
122
123
|
const record = normalize(value, line, source.locatorClass, threads.get(value.thread_id), value.request_id ? turns.get(`${value.thread_id}\0${value.request_id}`) : undefined);
|
|
123
124
|
if (outputIndex++ >= start)
|
|
124
125
|
yield record;
|
|
125
126
|
}
|
|
126
|
-
if (threads.size + turns.size > maxJoinStates) {
|
|
127
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OpenHuman enrichment join state exceeds the bounded streaming limit');
|
|
127
|
+
if (threads.size + turns.size + sessions.size > maxJoinStates) {
|
|
128
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'OpenHuman enrichment join state exceeds the bounded streaming limit', { limit: 'maxJoinStates', configured: maxJoinStates, observed: threads.size + turns.size + sessions.size });
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
if (!sawRaw) {
|
|
131
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman source contains no session_raw records');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async readSource(source) {
|
|
135
|
-
if (source.locatorClass !== 'openhuman-session-raw-jsonl'
|
|
136
|
-
&& source.locatorClass !== 'openhuman-enriched-jsonl') {
|
|
137
|
-
throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported OpenHuman source class');
|
|
138
|
-
}
|
|
139
|
-
const result = await readBoundedJsonLines({
|
|
140
|
-
selectedPath: source.locator,
|
|
141
|
-
allowedRoots: source.authorizedScope.allowedRoots,
|
|
142
|
-
}, { consistency: 'provisional', limits: this.limits });
|
|
143
|
-
if (result.records.length === 0) {
|
|
144
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman transcript source is empty');
|
|
145
|
-
}
|
|
146
|
-
const parsed = result.records.map(({ value, ...line }) => {
|
|
147
|
-
const record = RecordSchema.safeParse(value);
|
|
148
|
-
if (!record.success)
|
|
149
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman transcript record is malformed');
|
|
150
|
-
return { value: record.data, ...line };
|
|
151
|
-
});
|
|
152
|
-
const versions = new Set(parsed.map(({ value }) => version(value.schemaVersion)));
|
|
153
|
-
if (versions.size !== 1)
|
|
154
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'mixed OpenHuman schema versions');
|
|
155
|
-
assertSupportedSchemaMajor([...versions][0]);
|
|
156
|
-
const raw = parsed.filter((item) => item.value.recordType === 'session_raw');
|
|
157
|
-
if (raw.length === 0) {
|
|
158
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman source contains no session_raw records');
|
|
159
|
-
}
|
|
160
|
-
const threads = new Map();
|
|
161
|
-
const turns = new Map();
|
|
162
|
-
for (const item of parsed) {
|
|
163
|
-
if (item.value.recordType === 'thread_state')
|
|
164
|
-
threads.set(item.value.thread_id, item.value);
|
|
165
|
-
if (item.value.recordType === 'turn_state') {
|
|
166
|
-
turns.set(`${item.value.thread_id}\0${item.value.request_id}`, item.value);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const sessions = new Map();
|
|
170
|
-
for (const item of raw) {
|
|
171
|
-
const values = sessions.get(item.value.session_id) ?? [];
|
|
172
|
-
values.push(item.value);
|
|
173
|
-
sessions.set(item.value.session_id, values);
|
|
132
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'OpenHuman source contains no session_raw records', schemaIssueDetail('<root>'));
|
|
174
133
|
}
|
|
175
134
|
return {
|
|
176
|
-
|
|
177
|
-
consistency:
|
|
178
|
-
|
|
179
|
-
records: raw.map(({ value, ...line }) => normalize(value, line, source.locatorClass, threads.get(value.thread_id), value.request_id ? turns.get(`${value.thread_id}\0${value.request_id}`) : undefined)),
|
|
135
|
+
sourceSchemaVersion: OPENHUMAN_SOURCE_SCHEMA_VERSION,
|
|
136
|
+
consistency: !input.incompleteTail && [...sessions.values()].every(Boolean) ? 'complete' : 'provisional',
|
|
137
|
+
operationalState: 'available',
|
|
180
138
|
};
|
|
181
139
|
}
|
|
182
140
|
}
|
|
@@ -296,7 +254,7 @@ function parseCursor(value) {
|
|
|
296
254
|
if (!value)
|
|
297
255
|
return 0;
|
|
298
256
|
if (!/^\d+$/.test(value))
|
|
299
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OpenHuman cursor');
|
|
257
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid OpenHuman cursor', schemaIssueDetail('cursor'));
|
|
300
258
|
return Number(value);
|
|
301
259
|
}
|
|
302
260
|
const RAW_KEYS = new Set([
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { opendir } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
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 PI_ADAPTER_VERSION = '1.0.0';
|
|
7
7
|
export const PI_SOURCE_SCHEMA_VERSION = '3.0.0';
|
|
8
8
|
const Header = z.object({ type: z.literal('session'), version: z.number().int(), id: z.string().min(1),
|
|
@@ -29,7 +29,7 @@ export class PiSessionAdapter {
|
|
|
29
29
|
for (const root of [...scope.allowedRoots].sort()) {
|
|
30
30
|
for await (const locator of jsonlFiles(resolve(root))) {
|
|
31
31
|
if (++count > this.maxFiles)
|
|
32
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Pi discovery file limit exceeded');
|
|
32
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Pi discovery file limit exceeded', { limit: 'maxFiles', configured: this.maxFiles, observed: count });
|
|
33
33
|
yield { provider: 'pi', locator, locatorClass: 'pi-session-v3-jsonl' };
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -37,13 +37,16 @@ export class PiSessionAdapter {
|
|
|
37
37
|
async inspect(source) {
|
|
38
38
|
let input;
|
|
39
39
|
try {
|
|
40
|
-
input = await
|
|
40
|
+
input = await streamBoundedJsonLines({ selectedPath: source.locator,
|
|
41
41
|
allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
44
|
throw normalizeAuthorizationError(error);
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
let header;
|
|
47
|
+
for await (const line of input)
|
|
48
|
+
header ??= parseHeader(line.value);
|
|
49
|
+
header ??= parseHeader(undefined);
|
|
47
50
|
if (input.incompleteTail)
|
|
48
51
|
throw new SessionContractError('TRUNCATED_SOURCE', 'Pi session has a truncated JSONL tail');
|
|
49
52
|
return { sourceSchemaVersion: `${header.version}.0.0`, consistency: 'complete', operationalState: 'available' };
|
|
@@ -52,28 +55,38 @@ export class PiSessionAdapter {
|
|
|
52
55
|
let input;
|
|
53
56
|
try {
|
|
54
57
|
input = await streamBoundedJsonLines({ selectedPath: source.locator,
|
|
55
|
-
allowedRoots: source.authorizedScope.allowedRoots }, {
|
|
58
|
+
allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
|
|
56
59
|
}
|
|
57
60
|
catch (error) {
|
|
58
61
|
throw normalizeAuthorizationError(error);
|
|
59
62
|
}
|
|
60
63
|
let sessionId = '';
|
|
61
|
-
|
|
64
|
+
const value = cursor?.value || '0';
|
|
65
|
+
const byteCursor = value.startsWith('byte:');
|
|
66
|
+
const position = value.replace(/^byte:/, '');
|
|
67
|
+
if (!/^\d+$/.test(position))
|
|
68
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Pi cursor', schemaIssueDetail('cursor'));
|
|
69
|
+
const start = Number(position);
|
|
70
|
+
let end = 0;
|
|
62
71
|
const ids = new Set();
|
|
63
72
|
for await (const line of input) {
|
|
64
|
-
|
|
73
|
+
end = line.byteOffset + line.byteLength;
|
|
74
|
+
if (byteCursor && start > line.byteOffset && start < end) {
|
|
75
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Pi cursor is not a record boundary', schemaIssueDetail('cursor'));
|
|
76
|
+
}
|
|
77
|
+
if (line.sequence === 0) {
|
|
65
78
|
sessionId = parseHeader(line.value).id;
|
|
66
79
|
continue;
|
|
67
80
|
}
|
|
68
|
-
if (!sessionId)
|
|
69
|
-
sessionId = basename(source.locator, '.jsonl');
|
|
70
81
|
const parsed = Entry.safeParse(line.value);
|
|
71
82
|
if (!parsed.success)
|
|
72
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Pi session entry is malformed');
|
|
83
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Pi session entry is malformed', schemaRejectionDetail(parsed.error));
|
|
73
84
|
const entry = parsed.data;
|
|
74
85
|
if (ids.has(entry.id))
|
|
75
86
|
throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Pi session contains a duplicate entry id');
|
|
76
87
|
ids.add(entry.id);
|
|
88
|
+
if (byteCursor ? end <= start : line.sequence <= start)
|
|
89
|
+
continue;
|
|
77
90
|
const message = object(entry.message);
|
|
78
91
|
const role = string(message.role) ?? roleFor(entry.type);
|
|
79
92
|
const sensitive = role === 'toolResult' || entry.type === 'custom';
|
|
@@ -81,7 +94,7 @@ export class PiSessionAdapter {
|
|
|
81
94
|
yield { nativeSessionId: sessionId, nativeEventId: entry.id, sequence: line.sequence,
|
|
82
95
|
kind: kindFor(entry.type, role), role, toolName: sensitive ? string(message.toolName) : undefined,
|
|
83
96
|
toolCallId: sensitive ? string(message.toolCallId) : undefined,
|
|
84
|
-
occurredAt: entry.timestamp, text, sourceCursor:
|
|
97
|
+
occurredAt: entry.timestamp, text, sourceCursor: `byte:${end}`,
|
|
85
98
|
sourceBytes: line.byteLength, rawReference: { locatorClass: 'pi-session-v3-jsonl', offset: line.byteOffset },
|
|
86
99
|
activityBoundary: entry.type === 'compaction' ? 'continuation' : undefined,
|
|
87
100
|
activityBoundaryBasis: entry.type === 'compaction' ? 'pi-session:compaction' : undefined,
|
|
@@ -90,6 +103,8 @@ export class PiSessionAdapter {
|
|
|
90
103
|
opaque: !KNOWN.has(entry.type), redacted: sensitive,
|
|
91
104
|
nativeType: entry.type, parentId: entry.parentId } };
|
|
92
105
|
}
|
|
106
|
+
if (byteCursor && start > end)
|
|
107
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'Pi cursor exceeds source size', schemaIssueDetail('cursor'));
|
|
93
108
|
if (input.incompleteTail)
|
|
94
109
|
throw new SessionContractError('TRUNCATED_SOURCE', 'Pi session has a truncated JSONL tail');
|
|
95
110
|
}
|
|
@@ -99,7 +114,7 @@ const KNOWN = new Set(['message', 'thinking_level_change', 'model_change', 'comp
|
|
|
99
114
|
function parseHeader(value) {
|
|
100
115
|
const parsed = Header.safeParse(value);
|
|
101
116
|
if (!parsed.success)
|
|
102
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Pi session header is malformed');
|
|
117
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Pi session header is malformed', schemaRejectionDetail(parsed.error));
|
|
103
118
|
if (parsed.data.version !== 3)
|
|
104
119
|
throw new SessionContractError('UNKNOWN_SCHEMA_MAJOR', `unsupported Pi session version: ${parsed.data.version}`);
|
|
105
120
|
return parsed.data;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
|
-
import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
3
|
+
import { SessionContractError, schemaIssueDetail, assertSupportedSchemaMajor, } from '../contracts.js';
|
|
4
4
|
import { readBoundedText } from '../readers.js';
|
|
5
5
|
export const WARP_ADAPTER_VERSION = '1.0.0';
|
|
6
6
|
export const WARP_MARKDOWN_SCHEMA_VERSION = '1.0.0';
|
|
@@ -48,7 +48,7 @@ export class WarpSessionAdapter {
|
|
|
48
48
|
assertSupportedSchemaMajor(schemaVersion);
|
|
49
49
|
const blocks = parseBlocks(value);
|
|
50
50
|
if (blocks.length === 0) {
|
|
51
|
-
throw new SessionContractError('MALFORMED_SOURCE', 'Warp Markdown export contains no recognized user or assistant conversation blocks');
|
|
51
|
+
throw new SessionContractError('MALFORMED_SOURCE', 'Warp Markdown export contains no recognized user or assistant conversation blocks', schemaIssueDetail('headings'));
|
|
52
52
|
}
|
|
53
53
|
const stableSessionId = `manual:${createHash('sha256').update(value).digest('hex').slice(0, 32)}`;
|
|
54
54
|
const lifecycleEvidence = /<!--\s*warp-lifecycle:\s*complete\s*-->/i.test(value)
|
|
@@ -154,7 +154,7 @@ function parseCursor(value) {
|
|
|
154
154
|
if (!value)
|
|
155
155
|
return 0;
|
|
156
156
|
if (!/^\d+$/.test(value))
|
|
157
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Warp cursor');
|
|
157
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'invalid Warp cursor', schemaIssueDetail('cursor'));
|
|
158
158
|
return Number(value);
|
|
159
159
|
}
|
|
160
160
|
//# sourceMappingURL=warp.js.map
|