@aiwg/cli 2026.10.1 → 2026.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3183 -24
- package/bin/aiwg.mjs +135 -47
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/repair.js +9 -4
- 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 +78 -12
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- 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 +218 -5
- package/dist/src/cli/handlers/diagnose.js +13 -5
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/feedback.js +14 -5
- 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 +248 -30
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +169 -10
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +28 -0
- package/dist/src/cli/handlers/regenerate.js +14 -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 +25 -11
- package/dist/src/cli/handlers/script-runner.js +19 -8
- package/dist/src/cli/handlers/session.js +406 -62
- package/dist/src/cli/handlers/sessions.js +243 -76
- package/dist/src/cli/handlers/setup-manifest.js +474 -79
- package/dist/src/cli/handlers/setup.js +180 -39
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +60 -3
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +307 -55
- package/dist/src/cli/handlers/utilities.js +125 -26
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -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 +349 -35
- package/dist/src/config/cli.js +256 -23
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -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-health.mjs +14 -3
- 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 +38 -11
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/adapters/jev.js +75 -27
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +14 -7
- 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 +25 -6
- 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 +15 -14
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/index.js +1 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +942 -0
- 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/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -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/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +64 -18
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +15 -3
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +95 -5
- 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 +366 -56
- 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 +620 -85
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +17 -27
- package/dist/src/mcp/helpers.mjs +109 -4
- 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/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +248 -131
- package/dist/src/mcp/registry.mjs +169 -100
- 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 +127 -82
- 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-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +22 -3
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +50 -2
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +647 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +76 -14
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3183 -24
- package/dist/src/models/model-discovery.js +931 -67
- package/dist/src/models/provider-policy.js +719 -11
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +261 -0
- package/dist/src/models/worker-direct-readiness.js +550 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +144 -0
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +986 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +430 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -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 +53 -23
- 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/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- 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/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +47 -12
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +558 -0
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +49 -0
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +43 -9
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +434 -24
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
- package/dist/src/sessions/adapters/muse.js +200 -104
- 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 +30 -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 +25 -5
- package/dist/src/sessions/batch-import.js +72 -51
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +75 -2
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/grok-build-cli.js +67 -5
- 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 +84 -46
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +199 -63
- package/dist/src/sessions/workspace-discovery.js +315 -88
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- 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 +195 -14
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- 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 +85 -31
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- 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/dist/src/writing/fidelity.js +18 -1
- package/package.json +32 -2
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
- package/tools/_resolve-impl.mjs +39 -2
- package/tools/agents/deploy-agents.mjs +64 -23
- package/tools/agents/providers/base.mjs +153 -166
- 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/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +62 -49
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- 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 +360 -0
- package/tools/providers/omp-transport.mjs +194 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
- 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 +439 -0
- 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 +2916 -0
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { SESSION_CONTRACT_VERSION, SessionEventSchema, SessionSchema, assertSupportedSchemaMajor, sha256, stableEventId, stableSessionId, SessionContractError, } from './contracts.js';
|
|
2
|
-
import {
|
|
1
|
+
import { SESSION_CONTRACT_VERSION, SessionEventSchema, SessionSchema, assertSupportedSchemaMajor, sha256, stableEventId, stableSessionId, SessionContractError, schemaIssueDetail, assertSessionResourceLimit, } from './contracts.js';
|
|
2
|
+
import { assertImportByteLimit, DEFAULT_IMPORT_BYTE_LIMITS, resolveImportLimits } from './import-limits.js';
|
|
3
|
+
import { redactSessionValue, sanitizeSessionEvent, sanitizeNativeExtensions } from './policy.js';
|
|
3
4
|
import { fingerprintSourcePrefix } from './readers.js';
|
|
5
|
+
import { DEFAULT_INACTIVITY_THRESHOLD_MS } from './lifecycle.js';
|
|
4
6
|
import { classifySessionEventOrigin, deriveSessionIntent, } from './origin.js';
|
|
5
|
-
const
|
|
7
|
+
export const DEFAULT_IMPORT_LIMITS = {
|
|
6
8
|
maxRecords: 1_000_000,
|
|
7
|
-
|
|
8
|
-
maxTotalBytes: 1024 * 1024 * 1024,
|
|
9
|
+
...DEFAULT_IMPORT_BYTE_LIMITS,
|
|
9
10
|
batchSize: 1_000,
|
|
10
11
|
};
|
|
11
12
|
export class SessionImportFailure extends SessionContractError {
|
|
12
13
|
failureReceipt;
|
|
13
14
|
constructor(failureReceipt, cause) {
|
|
14
|
-
super(cause instanceof SessionContractError ? cause.code : 'IMPORT_INTERRUPTED', cause instanceof Error ? cause.message : 'session import failed');
|
|
15
|
+
super(cause instanceof SessionContractError ? cause.code : 'IMPORT_INTERRUPTED', cause instanceof Error ? cause.message : 'session import failed', cause instanceof SessionContractError ? cause.detail : undefined);
|
|
15
16
|
this.failureReceipt = failureReceipt;
|
|
17
|
+
this.failureReceipt = redactSessionValue(failureReceipt).value;
|
|
16
18
|
this.name = 'SessionImportFailure';
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -30,7 +32,9 @@ export class IncrementalSessionImporter {
|
|
|
30
32
|
if (request.workspaceId !== request.selectedSource.authorizedScope.workspaceId) {
|
|
31
33
|
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'session import workspace is outside the authorized source scope');
|
|
32
34
|
}
|
|
33
|
-
const limits = { ...
|
|
35
|
+
const limits = { ...DEFAULT_IMPORT_LIMITS, ...resolveImportLimits(undefined, {
|
|
36
|
+
maxRecordBytes: request.limits?.maxRecordBytes, maxSourceBytes: request.limits?.maxTotalBytes,
|
|
37
|
+
}), ...request.limits };
|
|
34
38
|
const previous = request.batchRunId
|
|
35
39
|
? this.repository.getBatchCheckpoint(request.source.sourceId, request.adapter.adapterVersion, request.batchRunId) ?? this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion)
|
|
36
40
|
: this.repository.getCheckpoint(request.source.sourceId, request.adapter.adapterVersion);
|
|
@@ -40,17 +44,17 @@ export class IncrementalSessionImporter {
|
|
|
40
44
|
: undefined;
|
|
41
45
|
const sourceGeneration = continuity?.sourceGeneration ?? sourceGenerationDigest(request);
|
|
42
46
|
if (previous?.sourceGeneration && previous.sourceGeneration !== sourceGeneration) {
|
|
43
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session source generation changed; explicit restart or migration is required');
|
|
47
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source generation changed; explicit restart or migration is required', schemaIssueDetail('sourceGeneration'));
|
|
44
48
|
}
|
|
45
49
|
if (previous?.adapterVersion && previous.adapterVersion !== request.adapter.adapterVersion) {
|
|
46
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint adapter version changed; checkpoint migration is required');
|
|
50
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint adapter version changed; checkpoint migration is required', schemaIssueDetail('adapterVersion'));
|
|
47
51
|
}
|
|
48
52
|
if (previous?.sourceSchemaVersion
|
|
49
53
|
&& previous.sourceSchemaVersion !== request.source.sourceSchemaVersion) {
|
|
50
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint source schema changed; checkpoint migration is required');
|
|
54
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint source schema changed; checkpoint migration is required', schemaIssueDetail('sourceSchemaVersion'));
|
|
51
55
|
}
|
|
52
56
|
if (previous?.policyVersion && previous.policyVersion !== request.policyVersion) {
|
|
53
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint policy version changed; checkpoint migration is required');
|
|
57
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session checkpoint policy version changed; checkpoint migration is required', schemaIssueDetail('policyVersion'));
|
|
54
58
|
}
|
|
55
59
|
const checkpoint = {
|
|
56
60
|
cursor: previous?.cursor ?? '',
|
|
@@ -65,6 +69,7 @@ export class IncrementalSessionImporter {
|
|
|
65
69
|
policyVersion: request.policyVersion,
|
|
66
70
|
continuity: continuity
|
|
67
71
|
? (!previous || previous.positionKind === 'byte-offset'
|
|
72
|
+
|| previous.positionKind === 'provider-native'
|
|
68
73
|
? continuity.outcome
|
|
69
74
|
: 'unverified')
|
|
70
75
|
: (previous ? 'unverified' : 'new-generation'),
|
|
@@ -75,6 +80,7 @@ export class IncrementalSessionImporter {
|
|
|
75
80
|
};
|
|
76
81
|
const receipts = [];
|
|
77
82
|
let batch = [];
|
|
83
|
+
let batchBytes = 0;
|
|
78
84
|
let batchStart = checkpoint.recordsRead;
|
|
79
85
|
let durableCheckpoint = { ...checkpoint };
|
|
80
86
|
const importStarted = performance.now();
|
|
@@ -86,12 +92,14 @@ export class IncrementalSessionImporter {
|
|
|
86
92
|
if (request.signal?.aborted) {
|
|
87
93
|
throw new SessionContractError('IMPORT_INTERRUPTED', 'session import was cancelled at a durable batch boundary');
|
|
88
94
|
}
|
|
95
|
+
const identities = batch.map(recordIdentity);
|
|
96
|
+
const batchDigests = identities.map(identity => identity.digest);
|
|
89
97
|
const runId = sha256([
|
|
90
98
|
request.source.sourceId, request.adapter.adapterVersion,
|
|
91
99
|
batchStart, checkpoint.recordsRead,
|
|
92
|
-
...
|
|
100
|
+
...batchDigests,
|
|
93
101
|
].join('\0'));
|
|
94
|
-
const normalized = normalizeBatch(request, batch, runId, sourceObservedAt);
|
|
102
|
+
const normalized = normalizeBatch(request, batch, batchDigests, runId, sourceObservedAt);
|
|
95
103
|
const run = {
|
|
96
104
|
contractVersion: SESSION_CONTRACT_VERSION,
|
|
97
105
|
importRunId: runId,
|
|
@@ -122,7 +130,7 @@ export class IncrementalSessionImporter {
|
|
|
122
130
|
receipt.metrics = {
|
|
123
131
|
records: batch.length,
|
|
124
132
|
normalizedEvents: normalized.events.length,
|
|
125
|
-
normalizedBytes:
|
|
133
|
+
normalizedBytes: identities.reduce((bytes, identity) => bytes + identity.bytes, 0),
|
|
126
134
|
durationMs: performance.now() - importStarted,
|
|
127
135
|
batchLatencyMs: performance.now() - flushStarted,
|
|
128
136
|
heapUsedBytes: peakHeap,
|
|
@@ -132,26 +140,40 @@ export class IncrementalSessionImporter {
|
|
|
132
140
|
receipts.push(receipt);
|
|
133
141
|
durableCheckpoint = { ...checkpoint };
|
|
134
142
|
batch = [];
|
|
143
|
+
batchBytes = 0;
|
|
135
144
|
batchStart = checkpoint.recordsRead;
|
|
136
145
|
};
|
|
137
146
|
try {
|
|
147
|
+
if (continuity)
|
|
148
|
+
assertImportByteLimit('maxTotalBytes', limits, continuity.size);
|
|
138
149
|
for await (const record of request.adapter.stream(request.selectedSource, previous ? { value: previous.cursor } : undefined)) {
|
|
139
150
|
if (request.signal?.aborted) {
|
|
140
151
|
throw new SessionContractError('IMPORT_INTERRUPTED', 'session import was cancelled and can resume from its durable checkpoint');
|
|
141
152
|
}
|
|
142
|
-
const bytes =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
const bytes = recordBytes(record);
|
|
154
|
+
assertImportByteLimit('maxRecordBytes', limits, bytes);
|
|
155
|
+
if (!continuity)
|
|
156
|
+
assertImportByteLimit('maxTotalBytes', limits, checkpoint.bytesRead + bytes);
|
|
157
|
+
assertSessionResourceLimit('maxRecords', limits.maxRecords, checkpoint.recordsRead + 1);
|
|
158
|
+
// Bound batches by bytes as well as record count for large records.
|
|
159
|
+
if (batchBytes + bytes > 4 * 1024 * 1024)
|
|
160
|
+
flush();
|
|
148
161
|
batch.push(record);
|
|
162
|
+
batchBytes += bytes;
|
|
149
163
|
checkpoint.recordsRead += 1;
|
|
150
164
|
if (record.sourceCursor?.startsWith('byte:')) {
|
|
151
165
|
checkpoint.positionKind = 'byte-offset';
|
|
152
166
|
checkpoint.cursor = record.sourceCursor;
|
|
153
167
|
checkpoint.bytesRead = Number(record.sourceCursor.slice(5));
|
|
154
168
|
}
|
|
169
|
+
else if (record.sourceCursor?.startsWith('native:')) {
|
|
170
|
+
// An opaque provider-native resume token (e.g. a monotonic native
|
|
171
|
+
// sequence) — never a byte offset. Stored verbatim; `bytesRead`
|
|
172
|
+
// still accumulates so resource limits remain meaningful.
|
|
173
|
+
checkpoint.positionKind = 'provider-native';
|
|
174
|
+
checkpoint.cursor = record.sourceCursor;
|
|
175
|
+
checkpoint.bytesRead += bytes;
|
|
176
|
+
}
|
|
155
177
|
else {
|
|
156
178
|
checkpoint.bytesRead += bytes;
|
|
157
179
|
checkpoint.cursor = String(checkpoint.recordsRead);
|
|
@@ -186,6 +208,7 @@ export class IncrementalSessionImporter {
|
|
|
186
208
|
: durableCheckpoint,
|
|
187
209
|
errorCode: error instanceof SessionContractError
|
|
188
210
|
? error.code : 'IMPORT_INTERRUPTED',
|
|
211
|
+
detail: error instanceof SessionContractError ? error.detail : undefined,
|
|
189
212
|
}, error);
|
|
190
213
|
}
|
|
191
214
|
if (request.publish !== false && request.source.consistency === 'complete') {
|
|
@@ -198,20 +221,30 @@ export class IncrementalSessionImporter {
|
|
|
198
221
|
return receipts;
|
|
199
222
|
}
|
|
200
223
|
}
|
|
201
|
-
|
|
224
|
+
/** Keep large temporary JSON strings out of the asynchronous import frame. */
|
|
225
|
+
function recordBytes(record) {
|
|
226
|
+
return Buffer.byteLength(JSON.stringify(record));
|
|
227
|
+
}
|
|
228
|
+
/** Compute flush-time identity once, preserving adapters that mutate yielded objects. */
|
|
229
|
+
function recordIdentity(record) {
|
|
230
|
+
const serialized = JSON.stringify(record);
|
|
231
|
+
return { bytes: Buffer.byteLength(serialized), digest: sha256(serialized) };
|
|
232
|
+
}
|
|
233
|
+
function normalizeBatch(request, records, digests, importRunId, sourceObservedAt) {
|
|
202
234
|
const sessions = new Map();
|
|
203
|
-
const events = records.map((record) => {
|
|
235
|
+
const events = records.map((record, index) => {
|
|
204
236
|
const sessionId = stableSessionId(request.source.provider, request.source.sourceId, record.nativeSessionId);
|
|
205
|
-
const redacted = redactSessionText(record.text);
|
|
206
237
|
const native = sanitizeNativeExtensions(record.extensions ?? {});
|
|
207
|
-
const
|
|
238
|
+
const lineage = request.source.provider === 'codex' && native.value.lineage
|
|
239
|
+
? { lineage: native.value.lineage } : {};
|
|
240
|
+
const digest = digests[index];
|
|
208
241
|
const origin = classifySessionEventOrigin(request.source.provider, record);
|
|
209
242
|
const lifecycle = sessionLifecycle(request, record, sourceObservedAt);
|
|
210
243
|
const lifecycleEvidence = sessionLifecycleEvidence(request, record, sourceObservedAt);
|
|
211
244
|
const extensions = {
|
|
212
|
-
[`native.${request.source.provider}`]:
|
|
245
|
+
[`native.${request.source.provider}`]: record.extensions ?? {},
|
|
213
246
|
};
|
|
214
|
-
const event = SessionEventSchema.parse({
|
|
247
|
+
const event = sanitizeSessionEvent(SessionEventSchema.parse({
|
|
215
248
|
contractVersion: SESSION_CONTRACT_VERSION,
|
|
216
249
|
eventId: stableEventId(request.source.provider, request.source.sourceId, record, digest),
|
|
217
250
|
sessionId, sourceId: request.source.sourceId, importRunId,
|
|
@@ -229,17 +262,13 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
|
|
|
229
262
|
model: record.model ?? null,
|
|
230
263
|
entities: record.entities ?? [],
|
|
231
264
|
extractionState: record.extractionState ?? null,
|
|
232
|
-
searchableText:
|
|
265
|
+
searchableText: record.text, digest, rawReference: record.rawReference,
|
|
233
266
|
adapterVersion: request.adapter.adapterVersion,
|
|
234
267
|
consistency: request.source.consistency,
|
|
235
|
-
sensitivity: {
|
|
236
|
-
classification: redacted.sensitivity === 'sensitive' || native.sensitivity === 'sensitive'
|
|
237
|
-
? 'sensitive' : 'none',
|
|
238
|
-
classes: [...new Set([...redacted.classes, ...native.classes])].sort(),
|
|
239
|
-
},
|
|
268
|
+
sensitivity: { classification: 'none', classes: [] },
|
|
240
269
|
opaque: !isKnownEventKind(record.kind),
|
|
241
270
|
extensions,
|
|
242
|
-
});
|
|
271
|
+
}));
|
|
243
272
|
if (!sessions.has(sessionId)) {
|
|
244
273
|
sessions.set(sessionId, SessionSchema.parse({
|
|
245
274
|
contractVersion: SESSION_CONTRACT_VERSION,
|
|
@@ -251,6 +280,7 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
|
|
|
251
280
|
sourceDigest: sha256(`${sourceGenerationDigest(request)}\0${record.nativeSessionId}`),
|
|
252
281
|
extensions: {
|
|
253
282
|
[`native.${request.source.provider}`]: {
|
|
283
|
+
...lineage,
|
|
254
284
|
lifecycleEvidence,
|
|
255
285
|
},
|
|
256
286
|
},
|
|
@@ -270,6 +300,7 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
|
|
|
270
300
|
session.lifecycle = selected.lifecycle;
|
|
271
301
|
session.extensions[nativeKey] = {
|
|
272
302
|
...nativeRecord,
|
|
303
|
+
...lineage,
|
|
273
304
|
lifecycleEvidence: selected.evidence,
|
|
274
305
|
};
|
|
275
306
|
}
|
|
@@ -278,7 +309,7 @@ function normalizeBatch(request, records, importRunId, sourceObservedAt) {
|
|
|
278
309
|
for (const session of sessions.values()) {
|
|
279
310
|
session.intent = deriveSessionIntent(events.filter((event) => event.sessionId === session.sessionId));
|
|
280
311
|
}
|
|
281
|
-
return { sessions: [...sessions.values()], events };
|
|
312
|
+
return { sessions: redactSessionValue([...sessions.values()]).value, events };
|
|
282
313
|
}
|
|
283
314
|
const KNOWN_EVENT_KINDS = new Set(['message', 'tool-call', 'tool-result', 'artifact', 'attachment', 'summary']);
|
|
284
315
|
function isKnownEventKind(kind) {
|
|
@@ -354,6 +385,7 @@ function sessionLifecycleEvidence(request, record, sourceObservedAt) {
|
|
|
354
385
|
state: 'active',
|
|
355
386
|
observedAt: observedAt ?? request.source.authorizedAt,
|
|
356
387
|
confidence: 'provisional',
|
|
388
|
+
thresholdMs,
|
|
357
389
|
};
|
|
358
390
|
}
|
|
359
391
|
function nativeLifecycle(extensions) {
|
|
@@ -407,7 +439,6 @@ function evidenceTimestamp(value) {
|
|
|
407
439
|
const parsed = Date.parse(String(value.observedAt ?? ''));
|
|
408
440
|
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
|
|
409
441
|
}
|
|
410
|
-
const DEFAULT_INACTIVITY_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
411
442
|
function inactivityThresholdMs(request) {
|
|
412
443
|
return request.inactivityThresholdMs ?? DEFAULT_INACTIVITY_THRESHOLD_MS;
|
|
413
444
|
}
|
|
@@ -435,33 +466,40 @@ async function sourceContinuity(request, previous) {
|
|
|
435
466
|
selectedPath: request.selectedSource.locator,
|
|
436
467
|
allowedRoots: request.selectedSource.authorizedScope.allowedRoots,
|
|
437
468
|
};
|
|
469
|
+
// Snapshot sources are rewritten wholesale on every save (no stable
|
|
470
|
+
// append-only byte region, and the file identity may legitimately change
|
|
471
|
+
// via rename-swap): skip the shrink, file-identity, and prefix-rewrite
|
|
472
|
+
// throws below. Idempotency instead rests on the adapter's own monotonic
|
|
473
|
+
// native cursor plus the catalog's identity-keyed inserts.
|
|
474
|
+
const isSnapshot = request.adapter.sourceContinuity === 'snapshot';
|
|
438
475
|
const skipPrefixBytes = await request.adapter.mutablePrefixBytes?.(request.selectedSource) ?? 0;
|
|
439
476
|
const metadata = await fingerprintSourcePrefix(authorization, 0);
|
|
440
|
-
if (previous?.sourceSize !== undefined) {
|
|
477
|
+
if (!isSnapshot && previous?.sourceSize !== undefined) {
|
|
441
478
|
if (metadata.size < previous.sourceSize) {
|
|
442
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session source was truncated before its durable checkpoint');
|
|
479
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source was truncated before its durable checkpoint', schemaIssueDetail('sourceSize'));
|
|
443
480
|
}
|
|
444
481
|
if (previous.sourceFileIdentity
|
|
445
482
|
&& previous.sourceFileIdentity !== metadata.fileIdentity) {
|
|
446
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session source file generation was replaced or rotated');
|
|
483
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source file generation was replaced or rotated', schemaIssueDetail('sourceFileIdentity'));
|
|
447
484
|
}
|
|
448
485
|
const priorPrefix = await fingerprintSourcePrefix(authorization, previous.sourceSize, skipPrefixBytes);
|
|
449
486
|
if (previous.prefixDigest && priorPrefix.digest !== previous.prefixDigest) {
|
|
450
|
-
throw new SessionContractError('SCHEMA_DRIFT', 'session source prefix was rewritten before its durable checkpoint');
|
|
487
|
+
throw new SessionContractError('SCHEMA_DRIFT', 'session source prefix was rewritten before its durable checkpoint', schemaIssueDetail('prefixDigest'));
|
|
451
488
|
}
|
|
452
489
|
}
|
|
453
490
|
const current = await fingerprintSourcePrefix(authorization, metadata.size, skipPrefixBytes);
|
|
454
|
-
const generation = previous?.sourceGeneration ??
|
|
455
|
-
sourceGenerationDigest(request)
|
|
456
|
-
metadata.fileIdentity
|
|
457
|
-
].join('\0'));
|
|
491
|
+
const generation = previous?.sourceGeneration ?? (isSnapshot
|
|
492
|
+
? sourceGenerationDigest(request)
|
|
493
|
+
: sha256([sourceGenerationDigest(request), metadata.fileIdentity].join('\0')));
|
|
458
494
|
return {
|
|
459
495
|
sourceGeneration: generation,
|
|
460
496
|
outcome: !previous
|
|
461
497
|
? 'new-generation'
|
|
462
|
-
:
|
|
463
|
-
|
|
464
|
-
|
|
498
|
+
: isSnapshot
|
|
499
|
+
? (current.digest === previous.prefixDigest ? 'unchanged-replay' : 'validated-append')
|
|
500
|
+
: metadata.size === previous.sourceSize
|
|
501
|
+
&& current.digest === previous.prefixDigest
|
|
502
|
+
? 'unchanged-replay' : 'validated-append',
|
|
465
503
|
size: metadata.size,
|
|
466
504
|
mtimeMs: metadata.mtimeMs,
|
|
467
505
|
fileIdentity: metadata.fileIdentity,
|
|
@@ -42,4 +42,6 @@ export * from './adapters/warp.js';
|
|
|
42
42
|
export * from './adapters/windsurf.js';
|
|
43
43
|
export * from './adapters/muse.js';
|
|
44
44
|
export * from './adapters/omp.js';
|
|
45
|
+
export * from './adapter-registry.js';
|
|
46
|
+
export * from './coverage-preflight.js';
|
|
45
47
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const DEFAULT_INACTIVITY_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
2
|
+
export function effectiveSessionLifecycle(session) {
|
|
3
|
+
if (session.lifecycle !== 'active' && session.lifecycle !== 'inactive')
|
|
4
|
+
return session;
|
|
5
|
+
const nativeKey = `native.${session.provider}`;
|
|
6
|
+
const native = objectRecord(session.extensions[nativeKey]);
|
|
7
|
+
const evidence = objectRecord(native.lifecycleEvidence);
|
|
8
|
+
if (evidence.basis !== 'open-provisional-source'
|
|
9
|
+
&& evidence.basis !== 'inactivity-threshold')
|
|
10
|
+
return session;
|
|
11
|
+
const observedAt = session.updatedAt ?? evidence.observedAt;
|
|
12
|
+
const timestamp = typeof observedAt === 'string' ? Date.parse(observedAt) : NaN;
|
|
13
|
+
if (!Number.isFinite(timestamp))
|
|
14
|
+
return session;
|
|
15
|
+
const thresholdMs = typeof evidence.thresholdMs === 'number'
|
|
16
|
+
&& Number.isFinite(evidence.thresholdMs)
|
|
17
|
+
&& evidence.thresholdMs >= 0
|
|
18
|
+
? evidence.thresholdMs : DEFAULT_INACTIVITY_THRESHOLD_MS;
|
|
19
|
+
const inactive = Date.now() - timestamp > thresholdMs;
|
|
20
|
+
const lifecycle = inactive ? 'inactive' : 'active';
|
|
21
|
+
return {
|
|
22
|
+
...session,
|
|
23
|
+
lifecycle,
|
|
24
|
+
extensions: {
|
|
25
|
+
...session.extensions,
|
|
26
|
+
[nativeKey]: {
|
|
27
|
+
...native,
|
|
28
|
+
lifecycleEvidence: {
|
|
29
|
+
...evidence,
|
|
30
|
+
basis: inactive ? 'inactivity-threshold' : 'open-provisional-source',
|
|
31
|
+
state: lifecycle,
|
|
32
|
+
observedAt,
|
|
33
|
+
confidence: inactive ? 'medium' : 'provisional',
|
|
34
|
+
thresholdMs,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function objectRecord(value) {
|
|
41
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
42
|
+
? value
|
|
43
|
+
: {};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -27,7 +27,9 @@ export async function authorizeSourceFile(input) {
|
|
|
27
27
|
throw new SessionContractError('SOURCE_NOT_REGULAR_FILE', 'session source must be a regular file');
|
|
28
28
|
}
|
|
29
29
|
if (input.maxBytes !== undefined && stat.size > input.maxBytes) {
|
|
30
|
-
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session source exceeds the authorized byte limit'
|
|
30
|
+
throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'session source exceeds the authorized byte limit', {
|
|
31
|
+
limit: 'maxBytes', configured: input.maxBytes, observed: stat.size,
|
|
32
|
+
});
|
|
31
33
|
}
|
|
32
34
|
return {
|
|
33
35
|
canonicalPath,
|
|
@@ -54,21 +56,51 @@ async function safeRealpath(path, root = false) {
|
|
|
54
56
|
throw new SessionContractError('SOURCE_NOT_AUTHORIZED', root ? 'an authorized source root is inaccessible' : 'selected session source is inaccessible');
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
// Ambiguous token components are bounded; whitespace and key blocks use disjoint scans.
|
|
57
60
|
const SECRET_PATTERNS = [
|
|
58
|
-
['
|
|
59
|
-
['
|
|
60
|
-
['
|
|
61
|
-
['
|
|
62
|
-
['credential', /\b(?:api[_-]?key|token|password|secret)\
|
|
63
|
-
['
|
|
61
|
+
['bearer-token', /\bBearer[ \t]+[A-Za-z0-9._~+/=-]{12,}/gi],
|
|
62
|
+
['authorization', /\bAuthorization[ \t]*:[ \t]*[^\r\n]+/gi],
|
|
63
|
+
['cookie', /\b(?:Cookie|Set-Cookie)[ \t]*:[ \t]*[^\r\n]+/gi],
|
|
64
|
+
['environment-assignment', /\b(?:AWS_SECRET_ACCESS_KEY|[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API_KEY))[ \t]*=[ \t]*(?:"[^"\r\n]+"|'[^'\r\n]+'|[^\s]+)/gi],
|
|
65
|
+
['credential', /\b(?:api[_-]?key|token|password|secret)[ \t]*[:=][ \t]*["']?[^\s"',;]{6,}/gi],
|
|
66
|
+
['credential', /^[ \t]*password[ \t]+[^\s]+[ \t]*$/gim],
|
|
67
|
+
// Space-separated values only when they look generated (20+ chars, letters and digits), so prose stays.
|
|
68
|
+
['credential', /\b(?:api[_-]?key|token|secret)[ \t]+(?=[A-Za-z0-9._~+/=-]*[0-9])(?=[A-Za-z0-9._~+/=-]*[A-Za-z])[A-Za-z0-9._~+/=-]{20,}/gi],
|
|
69
|
+
['provider-token', /\b(?:sk-(?:ant-|proj-)?|[sr]k_(?:live|test)_|gh[pousr]_|github_pat_|glpat-|xai-|npm_|hf_|xox[baprs]-|AKIA|AIza)[A-Za-z0-9._~+/=-]{8,}/g],
|
|
70
|
+
['provider-token', /https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+/g],
|
|
64
71
|
['connection-string', /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/[^\s]+/gi],
|
|
65
72
|
['credential-url', /\bhttps?:\/\/[^/\s:@]+:[^/\s@]+@[^\s]+/gi],
|
|
66
|
-
['environment-assignment', /\b[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API_KEY)\s*=\s*(?:"[^"]+"|'[^']+'|[^\s]+)/g],
|
|
67
73
|
['fixture-credential', /\bredaction-canary-[A-Za-z0-9_-]+\b/gi],
|
|
68
|
-
['email', /\b[A-Z0-9._%+-]
|
|
74
|
+
['email', /\b[A-Z0-9._%+-]{1,64}@[A-Z0-9.-]{1,253}\.[A-Z]{2,63}\b/gi],
|
|
69
75
|
];
|
|
76
|
+
function redactJwts(input, mark) {
|
|
77
|
+
// Consume even incomplete candidates. Retrying at each inner `eyJ` in an
|
|
78
|
+
// unterminated segment would repeatedly scan the remainder of a large record.
|
|
79
|
+
return input.replace(/\beyJ[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+){0,2}/g, (candidate) => {
|
|
80
|
+
const parts = candidate.split('.');
|
|
81
|
+
return parts.length === 3 && parts[0].length >= 8 && parts[1].length >= 2 && parts[2].length >= 8
|
|
82
|
+
? mark('provider-token') : candidate;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const PRIVATE_KEY_BEGIN = /-----BEGIN ((?:(?:RSA|EC|DSA|OPENSSH|ENCRYPTED) )?PRIVATE KEY|PGP PRIVATE KEY BLOCK)-----/g;
|
|
86
|
+
function redactPrivateKeys(input, mark) {
|
|
87
|
+
const parts = [];
|
|
88
|
+
let cursor = 0;
|
|
89
|
+
PRIVATE_KEY_BEGIN.lastIndex = 0;
|
|
90
|
+
let match;
|
|
91
|
+
while ((match = PRIVATE_KEY_BEGIN.exec(input))) {
|
|
92
|
+
parts.push(input.slice(cursor, match.index), mark('private-key'));
|
|
93
|
+
const endMarker = `-----END ${match[1]}-----`;
|
|
94
|
+
const end = input.indexOf(endMarker, PRIVATE_KEY_BEGIN.lastIndex);
|
|
95
|
+
// An unfinished key block is sensitive through the end of the record.
|
|
96
|
+
cursor = end < 0 ? input.length : end + endMarker.length;
|
|
97
|
+
PRIVATE_KEY_BEGIN.lastIndex = cursor;
|
|
98
|
+
}
|
|
99
|
+
parts.push(input.slice(cursor));
|
|
100
|
+
return parts.join('');
|
|
101
|
+
}
|
|
70
102
|
const SENSITIVE_KEY = /(?:authorization|auth[_-]?header|cookie|credential|password|passwd|secret|token|api[_-]?key|private[_-]?key|connection[_-]?string)/i;
|
|
71
|
-
const CONTENT_KEY = /(?:^|[_-])(?:text|content|prompt|command|arguments?|args|result|output|body|request|response|source|code|query)(?:$|[_-])/i;
|
|
103
|
+
const CONTENT_KEY = /(?:^|[_-])(?:data|text|content|prompt|command|arguments?|args|result|output|body|request|response|source|code|query)(?:$|[_-])/i;
|
|
72
104
|
const PATH_KEY = /(?:^|[_-])(?:path|cwd|directory|filename|file)(?:$|[_-])/i;
|
|
73
105
|
const SAFE_STRING_KEY = /(?:^|[_-])(?:id|kind|type|role|status|state|lifecycle|reason|name|product|provider|version|schema|model|format|class|mode|event|operation|tool|method|language|scope|consistency|disposition|phase|visibility|protocol|category|classification|decision|capability|permission|transition|hash|digest)(?:$|[_-])/i;
|
|
74
106
|
const CONTROL_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
|
|
@@ -103,23 +135,28 @@ export function sanitizeNativeExtensions(input, overrides = {}) {
|
|
|
103
135
|
return marker('depth-limit');
|
|
104
136
|
if (value === null || typeof value === 'boolean' || typeof value === 'number')
|
|
105
137
|
return value;
|
|
138
|
+
if (typeof value === 'string' && /^\[REDACTED:[a-z-]+\]$/.test(value))
|
|
139
|
+
return value;
|
|
140
|
+
if (SENSITIVE_KEY.test(classifiedKey))
|
|
141
|
+
return marker('sensitive-field');
|
|
142
|
+
if (CONTENT_KEY.test(classifiedKey))
|
|
143
|
+
return marker('content');
|
|
144
|
+
if (PATH_KEY.test(classifiedKey))
|
|
145
|
+
return marker('path');
|
|
106
146
|
if (typeof value === 'bigint')
|
|
107
147
|
return String(value);
|
|
108
148
|
if (typeof value !== 'string' && typeof value !== 'object') {
|
|
109
149
|
return marker('unsupported-value');
|
|
110
150
|
}
|
|
111
151
|
if (typeof value === 'string') {
|
|
112
|
-
if (SENSITIVE_KEY.test(classifiedKey))
|
|
113
|
-
return marker('sensitive-field');
|
|
114
|
-
if (CONTENT_KEY.test(classifiedKey))
|
|
115
|
-
return marker('content');
|
|
116
|
-
if (PATH_KEY.test(classifiedKey))
|
|
117
|
-
return marker('path');
|
|
118
152
|
if (Buffer.byteLength(value) > limits.maxStringBytes)
|
|
119
153
|
return marker('string-limit');
|
|
120
154
|
const redacted = redactSessionText(value);
|
|
121
155
|
for (const classification of redacted.classes)
|
|
122
156
|
classes.add(classification);
|
|
157
|
+
for (const [classification, count] of Object.entries(redacted.decisions)) {
|
|
158
|
+
decisions[classification] = (decisions[classification] ?? 0) + count;
|
|
159
|
+
}
|
|
123
160
|
if (redacted.classes.length) {
|
|
124
161
|
decisions['classified-value'] = (decisions['classified-value'] ?? 0) + 1;
|
|
125
162
|
return redacted.text.replace(CONTROL_CHARACTERS, ' ');
|
|
@@ -142,8 +179,15 @@ export function sanitizeNativeExtensions(input, overrides = {}) {
|
|
|
142
179
|
const output = {};
|
|
143
180
|
const entries = Object.entries(value);
|
|
144
181
|
for (const [childKey, childValue] of entries.slice(0, limits.maxObjectKeys)) {
|
|
145
|
-
|
|
146
|
-
|
|
182
|
+
const safeKey = redactSessionText(childKey);
|
|
183
|
+
for (const classification of safeKey.classes)
|
|
184
|
+
classes.add(classification);
|
|
185
|
+
for (const [classification, count] of Object.entries(safeKey.decisions)) {
|
|
186
|
+
decisions[classification] = (decisions[classification] ?? 0) + count;
|
|
187
|
+
}
|
|
188
|
+
Object.defineProperty(output, safeKey.text.replace(/\r?\n/g, ' ').replace(CONTROL_CHARACTERS, ''), {
|
|
189
|
+
value: visit(childValue, childKey, depth + 1), enumerable: true, configurable: true, writable: true,
|
|
190
|
+
});
|
|
147
191
|
}
|
|
148
192
|
if (entries.length > limits.maxObjectKeys)
|
|
149
193
|
output.__truncated__ = marker('object-key-limit');
|
|
@@ -165,15 +209,79 @@ export function sanitizeNativeExtensions(input, overrides = {}) {
|
|
|
165
209
|
};
|
|
166
210
|
}
|
|
167
211
|
export function redactSessionText(input) {
|
|
168
|
-
const
|
|
169
|
-
|
|
212
|
+
const decisions = {};
|
|
213
|
+
const mark = (classification) => {
|
|
214
|
+
decisions[classification] = (decisions[classification] ?? 0) + 1;
|
|
215
|
+
return `[REDACTED:${classification}]`;
|
|
216
|
+
};
|
|
217
|
+
let text = redactJwts(redactPrivateKeys(input, mark), mark);
|
|
170
218
|
for (const [classification, pattern] of SECRET_PATTERNS) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
219
|
+
// Every email match requires an at-sign. Avoid costly bounded local-part
|
|
220
|
+
// backtracking on large records containing only email-shaped near matches.
|
|
221
|
+
if (classification === 'email' && !text.includes('@'))
|
|
222
|
+
continue;
|
|
223
|
+
text = text.replace(pattern, () => mark(classification));
|
|
175
224
|
}
|
|
176
|
-
|
|
225
|
+
const classes = Object.keys(decisions).sort();
|
|
226
|
+
return { text, sensitivity: classes.length ? 'sensitive' : 'none', classes, decisions };
|
|
227
|
+
}
|
|
228
|
+
/** Redact every string, including object keys, without changing canonical field shapes. */
|
|
229
|
+
export function redactSessionValue(input) {
|
|
230
|
+
const decisions = {};
|
|
231
|
+
const active = new WeakSet();
|
|
232
|
+
const text = (value) => {
|
|
233
|
+
const result = redactSessionText(value);
|
|
234
|
+
for (const [key, count] of Object.entries(result.decisions)) {
|
|
235
|
+
decisions[key] = (decisions[key] ?? 0) + count;
|
|
236
|
+
}
|
|
237
|
+
return result.text;
|
|
238
|
+
};
|
|
239
|
+
const visit = (value, depth) => {
|
|
240
|
+
if (typeof value === 'string')
|
|
241
|
+
return text(value);
|
|
242
|
+
if (!value || typeof value !== 'object')
|
|
243
|
+
return value;
|
|
244
|
+
if (depth > 64 || active.has(value))
|
|
245
|
+
return '[REDACTED:structure-limit]';
|
|
246
|
+
active.add(value);
|
|
247
|
+
try {
|
|
248
|
+
return Array.isArray(value)
|
|
249
|
+
? value.map((item) => visit(item, depth + 1))
|
|
250
|
+
: Object.fromEntries(Object.entries(value).map(([key, item]) => [text(key), visit(item, depth + 1)]));
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
active.delete(value);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const value = visit(input, 0);
|
|
257
|
+
return { value, classes: Object.keys(decisions).sort(), decisions };
|
|
258
|
+
}
|
|
259
|
+
/** Shared import boundary for canonical text, metadata and bounded native attributes. */
|
|
260
|
+
export function sanitizeSessionEvent(input) {
|
|
261
|
+
const native = sanitizeNativeExtensions(input.extensions);
|
|
262
|
+
const result = redactSessionValue({ ...input, extensions: native.value });
|
|
263
|
+
const event = result.value;
|
|
264
|
+
const classes = [...new Set([...event.sensitivity.classes, ...native.classes, ...result.classes])].sort();
|
|
265
|
+
event.sensitivity = {
|
|
266
|
+
classification: classes.length ? 'sensitive' : 'none', classes,
|
|
267
|
+
};
|
|
268
|
+
// Counts contain classifications only, never matched input or excerpts.
|
|
269
|
+
const prior = event.extensions['native.redaction'];
|
|
270
|
+
const counts = {};
|
|
271
|
+
for (const item of Array.isArray(prior?.decisions) ? prior.decisions : []) {
|
|
272
|
+
if (item && typeof item.classification === 'string' && Number.isSafeInteger(item.count) && item.count >= 0) {
|
|
273
|
+
counts[item.classification] = item.count;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
for (const decisions of [native.decisions, result.decisions]) {
|
|
277
|
+
for (const [key, count] of Object.entries(decisions)) {
|
|
278
|
+
counts[key] = (counts[key] ?? 0) + count;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
event.extensions['native.redaction'] = {
|
|
282
|
+
decisions: Object.entries(counts).map(([classification, count]) => ({ classification, count })),
|
|
283
|
+
};
|
|
284
|
+
return event;
|
|
177
285
|
}
|
|
178
286
|
export function contentFreeAuditEvent(event) {
|
|
179
287
|
return Object.freeze({ ...event, counts: { ...event.counts } });
|