@aiwg/cli 2026.10.3 → 2026.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +69 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +286 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +130 -0
- package/dist/src/artifacts/index-refresh.js +591 -0
- package/dist/src/artifacts/index-worker.mjs +11 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +137 -43
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +95 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +252 -12
- package/dist/src/sessions/contracts.js +50 -3
- package/dist/src/sessions/coverage-preflight.js +27 -2
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +145 -26
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +105 -0
- package/dist/src/sessions/repository.js +151 -54
- package/dist/src/sessions/workspace-discovery.js +292 -60
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { requireFeaturePackage } from '../features/runtime.js';
|
|
2
2
|
import { CandidateReviewReceiptSchema, DeletionReceiptSchema, IntelligenceCandidateSchema, PromotionDependencyDecisionSchema, PromotionReceiptSchema, SessionContractError, sha256, } from './contracts.js';
|
|
3
3
|
import { coverageFromBatchRun, } from './batch-contracts.js';
|
|
4
|
-
import { redactSessionText, sanitizeNativeExtensions } from './policy.js';
|
|
4
|
+
import { redactSessionText, redactSessionValue, sanitizeSessionEvent, sanitizeNativeExtensions } from './policy.js';
|
|
5
|
+
import { effectiveSessionLifecycle } from './lifecycle.js';
|
|
5
6
|
import { deriveSessionAnalytics, } from './analytics.js';
|
|
6
7
|
const POLICY_PROVIDER_MIGRATION = 'policy-provider-identity:v2';
|
|
7
8
|
const EVENT_ORIGIN_INTENT_MIGRATION = 'event-origin-intent:v1';
|
|
@@ -12,7 +13,7 @@ const VISIBLE_SESSION_PREDICATE = `s.lifecycle != 'tombstoned' AND EXISTS (
|
|
|
12
13
|
)`;
|
|
13
14
|
export class SessionRepository {
|
|
14
15
|
db;
|
|
15
|
-
constructor(path = ':memory:') {
|
|
16
|
+
constructor(path = ':memory:', options = {}) {
|
|
16
17
|
let Database;
|
|
17
18
|
try {
|
|
18
19
|
Database = requireFeaturePackage('better-sqlite3');
|
|
@@ -22,7 +23,9 @@ export class SessionRepository {
|
|
|
22
23
|
'run `aiwg features install sqlite`, then retry. If the native build fails, install ' +
|
|
23
24
|
'Python 3, make, and a C/C++ compiler supported by node-gyp.', { cause });
|
|
24
25
|
}
|
|
25
|
-
this.db = new Database(path);
|
|
26
|
+
this.db = new Database(path, options.readonly ? { readonly: true, fileMustExist: true } : undefined);
|
|
27
|
+
if (options.readonly)
|
|
28
|
+
return;
|
|
26
29
|
this.db.pragma('foreign_keys = ON');
|
|
27
30
|
this.db.pragma('journal_mode = WAL');
|
|
28
31
|
this.db.pragma('busy_timeout = 5000');
|
|
@@ -124,6 +127,12 @@ export class SessionRepository {
|
|
|
124
127
|
ON sessions(workspace_id, source_id, lifecycle);
|
|
125
128
|
CREATE INDEX IF NOT EXISTS idx_event_session_sequence
|
|
126
129
|
ON session_events(session_id, sequence_no);
|
|
130
|
+
CREATE INDEX IF NOT EXISTS idx_event_source_run
|
|
131
|
+
ON session_events(source_id, import_run_id);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_event_import_run_session
|
|
133
|
+
ON session_events(import_run_id, session_id);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_session_analytics_session
|
|
135
|
+
ON session_analytics_facts(session_id);
|
|
127
136
|
CREATE INDEX IF NOT EXISTS idx_session_analytics_scope
|
|
128
137
|
ON session_analytics_facts(
|
|
129
138
|
workspace_id, category, provider, status, occurred_at, fact_id
|
|
@@ -131,6 +140,9 @@ export class SessionRepository {
|
|
|
131
140
|
CREATE VIRTUAL TABLE IF NOT EXISTS session_event_fts USING fts5(
|
|
132
141
|
event_id UNINDEXED, searchable_text, tokenize='unicode61'
|
|
133
142
|
);
|
|
143
|
+
-- FTS5 stores event_id in c0. A covering index checks identity/rowid
|
|
144
|
+
-- alignment without reading searchable text or event JSON.
|
|
145
|
+
CREATE INDEX IF NOT EXISTS idx_fts_event_id ON session_event_fts_content(c0);
|
|
134
146
|
`);
|
|
135
147
|
const deletionReceiptColumns = this.db.prepare('PRAGMA table_info(deletion_receipts)').all().map((row) => String(row.name));
|
|
136
148
|
if (!deletionReceiptColumns.includes('workspace_id')) {
|
|
@@ -154,48 +166,79 @@ export class SessionRepository {
|
|
|
154
166
|
CREATE INDEX IF NOT EXISTS idx_mutation_audit_workspace
|
|
155
167
|
ON mutation_audit(workspace_id, occurred_at, operation_id)
|
|
156
168
|
;
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_import_runs_batch
|
|
170
|
+
ON import_runs(batch_run_id);
|
|
157
171
|
CREATE INDEX IF NOT EXISTS idx_batch_import_workspace
|
|
158
172
|
ON batch_import_runs(workspace_id, updated_at, batch_run_id)
|
|
159
173
|
`);
|
|
174
|
+
this.initializeAnalyticsVerification();
|
|
160
175
|
this.alignSessionFtsRowids();
|
|
161
176
|
this.migrateSessionPolicyAndProviderIdentity();
|
|
162
177
|
this.migrateEventOriginAndIntent();
|
|
163
178
|
this.alignSessionAnalytics();
|
|
164
179
|
}
|
|
180
|
+
initializeAnalyticsVerification() {
|
|
181
|
+
// Invalidate verification even for writes from another connection. Ordinary
|
|
182
|
+
// repository mutations maintain analytics in the same transaction below.
|
|
183
|
+
this.db.transaction(() => {
|
|
184
|
+
this.db.prepare('INSERT OR IGNORE INTO session_catalog_meta(key, value) VALUES (?, ?)').run('consistency:analytics:v1', 'dirty');
|
|
185
|
+
for (const table of [
|
|
186
|
+
'session_events', 'session_analytics_facts', 'sessions', 'session_sources', 'import_runs',
|
|
187
|
+
]) {
|
|
188
|
+
for (const operation of ['INSERT', 'UPDATE', 'DELETE']) {
|
|
189
|
+
this.db.exec(`
|
|
190
|
+
CREATE TRIGGER IF NOT EXISTS consistency_${table}_${operation.toLowerCase()}
|
|
191
|
+
AFTER ${operation} ON ${table}
|
|
192
|
+
BEGIN
|
|
193
|
+
UPDATE session_catalog_meta SET value='dirty'
|
|
194
|
+
WHERE key='consistency:analytics:v1' AND value!='dirty';
|
|
195
|
+
END;
|
|
196
|
+
`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
})();
|
|
200
|
+
}
|
|
201
|
+
analyticsVerified() {
|
|
202
|
+
return this.db.prepare('SELECT value FROM session_catalog_meta WHERE key=?').get('consistency:analytics:v1')?.value === 'verified';
|
|
203
|
+
}
|
|
204
|
+
markAnalyticsVerified() {
|
|
205
|
+
this.db.prepare('UPDATE session_catalog_meta SET value=? WHERE key=?').run('verified', 'consistency:analytics:v1');
|
|
206
|
+
}
|
|
207
|
+
maintainedAnalyticsTransaction(mutate) {
|
|
208
|
+
return this.db.transaction(() => {
|
|
209
|
+
const verified = this.analyticsVerified();
|
|
210
|
+
const result = mutate();
|
|
211
|
+
// A scoped mutation must not hide pre-existing damage elsewhere.
|
|
212
|
+
if (verified)
|
|
213
|
+
this.markAnalyticsVerified();
|
|
214
|
+
return result;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
165
217
|
alignSessionAnalytics() {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const factSessions = Number(this.db.prepare('SELECT COUNT(DISTINCT session_id) AS count FROM session_analytics_facts').get()?.count ?? 0);
|
|
172
|
-
const sourceSessions = Number(this.db.prepare(`SELECT COUNT(DISTINCT e.session_id) AS count
|
|
173
|
-
FROM session_events e
|
|
174
|
-
JOIN import_runs r ON r.import_run_id=e.import_run_id
|
|
175
|
-
JOIN sessions s ON s.session_id=e.session_id
|
|
176
|
-
WHERE r.status='committed' AND s.lifecycle!='tombstoned'`).get()?.count ?? 0);
|
|
177
|
-
if (eventCount === 0 || factSessions === sourceSessions)
|
|
178
|
-
return;
|
|
179
|
-
this.rebuildAnalytics();
|
|
218
|
+
this.db.transaction(() => {
|
|
219
|
+
if (this.analyticsVerified())
|
|
220
|
+
return;
|
|
221
|
+
this.rebuildAnalytics();
|
|
222
|
+
})();
|
|
180
223
|
}
|
|
181
224
|
alignSessionFtsRowids() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
225
|
+
this.db.transaction(() => {
|
|
226
|
+
const eventCount = Number(this.db.prepare('SELECT COUNT(*) AS count FROM session_events').get()?.count ?? 0);
|
|
227
|
+
const ftsCount = Number(this.db.prepare('SELECT COUNT(*) AS count FROM session_event_fts_content').get()?.count ?? 0);
|
|
228
|
+
const mismatch = this.db.prepare(`SELECT 1 AS mismatch
|
|
229
|
+
FROM session_event_fts_content f
|
|
230
|
+
LEFT JOIN session_events e ON e.event_id=f.c0
|
|
231
|
+
WHERE e.rowid IS NULL OR e.rowid!=f.id
|
|
232
|
+
LIMIT 1`).get();
|
|
233
|
+
if (eventCount === ftsCount && !mismatch)
|
|
234
|
+
return;
|
|
191
235
|
this.db.exec('DELETE FROM session_event_fts');
|
|
192
236
|
this.db.exec(`
|
|
193
237
|
INSERT INTO session_event_fts(rowid, event_id, searchable_text)
|
|
194
238
|
SELECT rowid, event_id, json_extract(data, '$.searchableText')
|
|
195
239
|
FROM session_events
|
|
196
240
|
`);
|
|
197
|
-
});
|
|
198
|
-
rebuild();
|
|
241
|
+
})();
|
|
199
242
|
}
|
|
200
243
|
migrateSessionPolicyAndProviderIdentity() {
|
|
201
244
|
this.runCatalogMigration(POLICY_PROVIDER_MIGRATION, () => {
|
|
@@ -331,6 +374,7 @@ export class SessionRepository {
|
|
|
331
374
|
transaction();
|
|
332
375
|
}
|
|
333
376
|
emitMutation(input) {
|
|
377
|
+
input = redactSessionValue(input).value;
|
|
334
378
|
const unsigned = {
|
|
335
379
|
contractVersion: '1.0.0',
|
|
336
380
|
...input,
|
|
@@ -435,7 +479,17 @@ export class SessionRepository {
|
|
|
435
479
|
};
|
|
436
480
|
}
|
|
437
481
|
applyImport(batch, checkpoint, publish = true) {
|
|
438
|
-
|
|
482
|
+
// Guard callers that submit normalized batches directly as well as adapters.
|
|
483
|
+
batch = redactSessionValue({
|
|
484
|
+
...batch,
|
|
485
|
+
source: { ...batch.source, extensions: sanitizeNativeExtensions(batch.source.extensions).value },
|
|
486
|
+
sessions: batch.sessions.map((session) => ({
|
|
487
|
+
...session, extensions: sanitizeSessionExtensions(session.extensions, session.provider),
|
|
488
|
+
})),
|
|
489
|
+
events: batch.events.map(sanitizeSessionEvent),
|
|
490
|
+
}).value;
|
|
491
|
+
checkpoint = redactSessionValue(checkpoint).value;
|
|
492
|
+
const apply = this.maintainedAnalyticsTransaction(() => {
|
|
439
493
|
const existing = this.db.prepare('SELECT outcome, counts, checkpoint FROM import_receipts WHERE import_run_id = ?').get(batch.run.importRunId);
|
|
440
494
|
if (existing) {
|
|
441
495
|
const counts = JSON.parse(String(existing.counts));
|
|
@@ -541,7 +595,7 @@ export class SessionRepository {
|
|
|
541
595
|
return apply();
|
|
542
596
|
}
|
|
543
597
|
commitStagedImports(sourceId, parserVersion) {
|
|
544
|
-
const commit = this.
|
|
598
|
+
const commit = this.maintainedAnalyticsTransaction(() => {
|
|
545
599
|
const staged = this.db.prepare(`SELECT import_run_id FROM import_runs
|
|
546
600
|
WHERE source_id=? AND parser_version=? AND status='staged'`).all(sourceId, parserVersion).map((row) => String(row.import_run_id));
|
|
547
601
|
const result = this.db.prepare(`UPDATE import_runs SET status='committed'
|
|
@@ -569,7 +623,7 @@ export class SessionRepository {
|
|
|
569
623
|
WHERE operation_id=?`).run(updated.observedAt, JSON.stringify(updated), operationId);
|
|
570
624
|
}
|
|
571
625
|
if (result.changes > 0)
|
|
572
|
-
this.
|
|
626
|
+
this.rebuildImportAnalytics(staged);
|
|
573
627
|
return result.changes;
|
|
574
628
|
});
|
|
575
629
|
return commit();
|
|
@@ -578,7 +632,7 @@ export class SessionRepository {
|
|
|
578
632
|
if (sourceIds.length === 0)
|
|
579
633
|
return 0;
|
|
580
634
|
const placeholders = sourceIds.map(() => '?').join(', ');
|
|
581
|
-
const commit = this.
|
|
635
|
+
const commit = this.maintainedAnalyticsTransaction(() => {
|
|
582
636
|
const params = [batchRunId, ...sourceIds];
|
|
583
637
|
const staged = this.db.prepare(`SELECT import_run_id FROM import_runs
|
|
584
638
|
WHERE batch_run_id=? AND source_id IN (${placeholders}) AND status='staged'`).all(...params).map((row) => String(row.import_run_id));
|
|
@@ -607,28 +661,39 @@ export class SessionRepository {
|
|
|
607
661
|
WHERE operation_id=?`).run(updated.observedAt, JSON.stringify(updated), operationId);
|
|
608
662
|
}
|
|
609
663
|
if (result.changes > 0)
|
|
610
|
-
this.
|
|
664
|
+
this.rebuildImportAnalytics(staged);
|
|
611
665
|
return result.changes;
|
|
612
666
|
});
|
|
613
667
|
return commit();
|
|
614
668
|
}
|
|
615
|
-
|
|
616
|
-
const
|
|
669
|
+
rebuildImportAnalytics(importRunIds) {
|
|
670
|
+
const affected = this.db.prepare(`SELECT DISTINCT s.workspace_id, s.session_id
|
|
617
671
|
FROM session_events e
|
|
618
|
-
JOIN
|
|
619
|
-
WHERE e.
|
|
620
|
-
const sessions =
|
|
672
|
+
JOIN sessions s ON s.session_id=e.session_id
|
|
673
|
+
WHERE e.import_run_id=?`);
|
|
674
|
+
const sessions = new Map();
|
|
675
|
+
for (const importRunId of importRunIds) {
|
|
676
|
+
for (const row of affected.all(importRunId)) {
|
|
677
|
+
sessions.set(String(row.session_id), String(row.workspace_id));
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
for (const [sessionId, workspaceId] of sessions) {
|
|
681
|
+
this.rebuildAnalytics(workspaceId, sessionId);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
sourceImportTotals(sourceId, batchRunId) {
|
|
685
|
+
const totals = this.db.prepare(`SELECT COUNT(*) AS events, COUNT(DISTINCT e.session_id) AS sessions
|
|
621
686
|
FROM session_events e
|
|
622
687
|
JOIN import_runs r ON r.import_run_id=e.import_run_id
|
|
623
|
-
WHERE e.source_id=? AND r.batch_run_id=?`).get(sourceId, batchRunId)
|
|
624
|
-
return { sessions, events };
|
|
688
|
+
WHERE e.source_id=? AND r.batch_run_id=?`).get(sourceId, batchRunId);
|
|
689
|
+
return { sessions: Number(totals?.sessions ?? 0), events: Number(totals?.events ?? 0) };
|
|
625
690
|
}
|
|
626
691
|
getSession(id, workspaceId) {
|
|
627
692
|
const row = this.db.prepare(`SELECT s.data FROM sessions s
|
|
628
693
|
WHERE s.session_id=? AND ${VISIBLE_SESSION_PREDICATE}
|
|
629
694
|
${workspaceId ? 'AND s.workspace_id=?' : ''}`)
|
|
630
695
|
.get(...(workspaceId ? [id, workspaceId] : [id]));
|
|
631
|
-
return row ? JSON.parse(String(row.data)) : null;
|
|
696
|
+
return row ? effectiveSessionLifecycle(JSON.parse(String(row.data))) : null;
|
|
632
697
|
}
|
|
633
698
|
findSessionsByReference(reference, workspaceId) {
|
|
634
699
|
const internal = this.getSession(reference, workspaceId);
|
|
@@ -637,7 +702,7 @@ export class SessionRepository {
|
|
|
637
702
|
const query = (predicate) => this.db.prepare(`SELECT s.data FROM sessions s
|
|
638
703
|
WHERE ${workspaceId ? 's.workspace_id=? AND' : ''} ${VISIBLE_SESSION_PREDICATE} AND (${predicate})
|
|
639
704
|
ORDER BY s.session_id`).all(...(workspaceId ? [workspaceId, reference] : [reference]))
|
|
640
|
-
.map((row) => JSON.parse(String(row.data)));
|
|
705
|
+
.map((row) => effectiveSessionLifecycle(JSON.parse(String(row.data))));
|
|
641
706
|
const exact = query('s.native_session_id=?');
|
|
642
707
|
if (exact.length === 1)
|
|
643
708
|
return exact;
|
|
@@ -669,7 +734,7 @@ export class SessionRepository {
|
|
|
669
734
|
OR substr(s.session_id, 1, length(?))=?)
|
|
670
735
|
ORDER BY s.session_id`).all(...(workspaceId ? [workspaceId, reference, reference, reference, reference]
|
|
671
736
|
: [reference, reference, reference, reference]))
|
|
672
|
-
.map((row) => JSON.parse(String(row.data)));
|
|
737
|
+
.map((row) => effectiveSessionLifecycle(JSON.parse(String(row.data))));
|
|
673
738
|
}
|
|
674
739
|
listWorkspaceSessionCounts() {
|
|
675
740
|
return this.db.prepare(`SELECT s.workspace_id, COUNT(*) AS count FROM sessions s
|
|
@@ -682,6 +747,7 @@ export class SessionRepository {
|
|
|
682
747
|
return this.db.prepare('SELECT DISTINCT workspace_id FROM sessions ORDER BY workspace_id').all().map((row) => String(row.workspace_id));
|
|
683
748
|
}
|
|
684
749
|
saveBatchImportRun(run) {
|
|
750
|
+
run = redactSessionValue(run).value;
|
|
685
751
|
const save = this.db.transaction(() => {
|
|
686
752
|
const prior = this.db.prepare('SELECT manifest_id, workspace_id FROM batch_import_runs WHERE batch_run_id=?').get(run.runId);
|
|
687
753
|
if (prior && (String(prior.manifest_id) !== run.manifestId
|
|
@@ -700,6 +766,22 @@ export class SessionRepository {
|
|
|
700
766
|
const row = this.db.prepare('SELECT data FROM batch_import_runs WHERE batch_run_id=?').get(runId);
|
|
701
767
|
return row ? JSON.parse(String(row.data)) : null;
|
|
702
768
|
}
|
|
769
|
+
/** Call only while holding the catalog import lease, before starting a new import. */
|
|
770
|
+
interruptRunningBatchImports(now = new Date()) {
|
|
771
|
+
const interruptedAt = now.toISOString();
|
|
772
|
+
return this.db.transaction(() => {
|
|
773
|
+
const rows = this.db.prepare("SELECT data FROM batch_import_runs WHERE status='running'").all();
|
|
774
|
+
return rows.map((row) => {
|
|
775
|
+
const run = JSON.parse(String(row.data));
|
|
776
|
+
run.status = 'interrupted';
|
|
777
|
+
run.interruptedAt = interruptedAt;
|
|
778
|
+
run.updatedAt = interruptedAt;
|
|
779
|
+
run.completedAt = null;
|
|
780
|
+
this.saveBatchImportRun(run);
|
|
781
|
+
return { runId: run.runId, interruptedAt };
|
|
782
|
+
});
|
|
783
|
+
})();
|
|
784
|
+
}
|
|
703
785
|
getBatchImportRunForManifest(manifestId, workspaceId) {
|
|
704
786
|
const row = this.db.prepare(`SELECT data FROM batch_import_runs
|
|
705
787
|
WHERE manifest_id=? AND workspace_id=?`).get(manifestId, workspaceId);
|
|
@@ -743,18 +825,18 @@ export class SessionRepository {
|
|
|
743
825
|
const pageParams = [...params];
|
|
744
826
|
if (cursor) {
|
|
745
827
|
pageWhere.push(`(
|
|
746
|
-
COALESCE(json_extract(s.data, '$.updatedAt'), '')
|
|
828
|
+
COALESCE(json_extract(s.data, '$.updatedAt'), '') < ?
|
|
747
829
|
OR (
|
|
748
830
|
COALESCE(json_extract(s.data, '$.updatedAt'), '') = ?
|
|
749
|
-
AND s.session_id
|
|
831
|
+
AND s.session_id < ?
|
|
750
832
|
)
|
|
751
833
|
)`);
|
|
752
834
|
pageParams.push(cursor.updatedAt, cursor.updatedAt, cursor.sessionId);
|
|
753
835
|
}
|
|
754
836
|
const items = this.db.prepare(`SELECT s.data FROM sessions s WHERE ${pageWhere.join(' AND ')}
|
|
755
|
-
ORDER BY COALESCE(json_extract(s.data, '$.updatedAt'), ''), s.session_id
|
|
837
|
+
ORDER BY COALESCE(json_extract(s.data, '$.updatedAt'), '') DESC, s.session_id DESC
|
|
756
838
|
LIMIT ?${options.offset ? ' OFFSET ?' : ''}`).all(...pageParams, options.limit + 1, ...(options.offset ? [options.offset] : []))
|
|
757
|
-
.map((row) => JSON.parse(String(row.data)));
|
|
839
|
+
.map((row) => effectiveSessionLifecycle(JSON.parse(String(row.data))));
|
|
758
840
|
const page = items.slice(0, options.limit);
|
|
759
841
|
const last = page.at(-1);
|
|
760
842
|
return {
|
|
@@ -779,6 +861,7 @@ export class SessionRepository {
|
|
|
779
861
|
.map((row) => String(row.tag));
|
|
780
862
|
}
|
|
781
863
|
tagSession(sessionId, tag, workspaceId) {
|
|
864
|
+
tag = redactSessionText(tag).text;
|
|
782
865
|
const mutate = this.db.transaction(() => {
|
|
783
866
|
if (!this.getSession(sessionId, workspaceId))
|
|
784
867
|
return false;
|
|
@@ -819,7 +902,7 @@ export class SessionRepository {
|
|
|
819
902
|
e.session_id,
|
|
820
903
|
e.sequence_no,
|
|
821
904
|
e.event_id`).all(workspaceId).map((row) => ({
|
|
822
|
-
session: JSON.parse(String(row.session_data)),
|
|
905
|
+
session: effectiveSessionLifecycle(JSON.parse(String(row.session_data))),
|
|
823
906
|
event: JSON.parse(String(row.event_data)),
|
|
824
907
|
}));
|
|
825
908
|
}
|
|
@@ -960,6 +1043,14 @@ export class SessionRepository {
|
|
|
960
1043
|
};
|
|
961
1044
|
}
|
|
962
1045
|
rebuildAnalytics(workspaceId, sessionId) {
|
|
1046
|
+
return this.maintainedAnalyticsTransaction(() => {
|
|
1047
|
+
const inserted = this.rebuildAnalyticsFacts(workspaceId, sessionId);
|
|
1048
|
+
if (!workspaceId && !sessionId)
|
|
1049
|
+
this.markAnalyticsVerified();
|
|
1050
|
+
return inserted;
|
|
1051
|
+
})();
|
|
1052
|
+
}
|
|
1053
|
+
rebuildAnalyticsFacts(workspaceId, sessionId) {
|
|
963
1054
|
const where = [
|
|
964
1055
|
`r.status='committed'`,
|
|
965
1056
|
`s.lifecycle!='tombstoned'`,
|
|
@@ -1128,7 +1219,10 @@ export class SessionRepository {
|
|
|
1128
1219
|
}
|
|
1129
1220
|
}
|
|
1130
1221
|
const saved = candidates.map((input) => {
|
|
1131
|
-
const
|
|
1222
|
+
const redacted = redactSessionValue(input);
|
|
1223
|
+
const parsed = IntelligenceCandidateSchema.parse(redacted.value);
|
|
1224
|
+
if (redacted.classes.length)
|
|
1225
|
+
parsed.sensitivity = 'sensitive';
|
|
1132
1226
|
this.assertCandidateEvidence(parsed);
|
|
1133
1227
|
const priorRow = this.db.prepare(`SELECT data FROM intelligence_candidates
|
|
1134
1228
|
WHERE candidate_id=? ORDER BY version DESC LIMIT 1`).get(parsed.candidateId);
|
|
@@ -1196,6 +1290,7 @@ export class SessionRepository {
|
|
|
1196
1290
|
return rows.map((row) => IntelligenceCandidateSchema.parse(JSON.parse(String(row.data))));
|
|
1197
1291
|
}
|
|
1198
1292
|
reviewCandidate(input) {
|
|
1293
|
+
input = redactSessionValue(input).value;
|
|
1199
1294
|
const review = this.db.transaction(() => {
|
|
1200
1295
|
const candidate = this.getCandidate(input.candidateId, input.version, input.workspaceId);
|
|
1201
1296
|
if (!candidate) {
|
|
@@ -1257,7 +1352,7 @@ export class SessionRepository {
|
|
|
1257
1352
|
}
|
|
1258
1353
|
recordPromotion(receiptInput) {
|
|
1259
1354
|
const record = this.db.transaction(() => {
|
|
1260
|
-
const receipt = PromotionReceiptSchema.parse(receiptInput);
|
|
1355
|
+
const receipt = PromotionReceiptSchema.parse(redactSessionValue(receiptInput).value);
|
|
1261
1356
|
const existing = this.getPromotionReceipt(receipt.candidateId, receipt.candidateVersion, receipt.consumer);
|
|
1262
1357
|
if (existing)
|
|
1263
1358
|
return { ...existing, duplicate: true };
|
|
@@ -1324,6 +1419,7 @@ export class SessionRepository {
|
|
|
1324
1419
|
return row ? JSON.parse(String(row.checkpoint)) : null;
|
|
1325
1420
|
}
|
|
1326
1421
|
relocateSource(sourceId, redactedLocator, workspaceId) {
|
|
1422
|
+
redactedLocator = redactSessionText(redactedLocator).text;
|
|
1327
1423
|
const relocate = this.db.transaction(() => {
|
|
1328
1424
|
const row = this.db.prepare(`SELECT src.data FROM session_sources src
|
|
1329
1425
|
WHERE src.source_id=? ${workspaceId ? `AND EXISTS (
|
|
@@ -1381,7 +1477,7 @@ export class SessionRepository {
|
|
|
1381
1477
|
};
|
|
1382
1478
|
}
|
|
1383
1479
|
tombstoneSession(sessionId, workspaceId) {
|
|
1384
|
-
const tombstone = this.
|
|
1480
|
+
const tombstone = this.maintainedAnalyticsTransaction(() => {
|
|
1385
1481
|
const row = this.db.prepare(`SELECT data, workspace_id FROM sessions
|
|
1386
1482
|
WHERE session_id=? ${workspaceId ? 'AND workspace_id=?' : ''}`).get(...(workspaceId ? [sessionId, workspaceId] : [sessionId]));
|
|
1387
1483
|
if (!row)
|
|
@@ -1410,7 +1506,7 @@ export class SessionRepository {
|
|
|
1410
1506
|
return tombstone();
|
|
1411
1507
|
}
|
|
1412
1508
|
restoreSession(sessionId, workspaceId) {
|
|
1413
|
-
const restore = this.
|
|
1509
|
+
const restore = this.maintainedAnalyticsTransaction(() => {
|
|
1414
1510
|
const row = this.db.prepare(`SELECT data, workspace_id FROM sessions
|
|
1415
1511
|
WHERE session_id=? ${workspaceId ? 'AND workspace_id=?' : ''}`).get(...(workspaceId ? [sessionId, workspaceId] : [sessionId]));
|
|
1416
1512
|
if (!row)
|
|
@@ -1524,6 +1620,7 @@ export class SessionRepository {
|
|
|
1524
1620
|
return rows.map((row) => JSON.parse(String(row.data)));
|
|
1525
1621
|
}
|
|
1526
1622
|
purgeSession(input) {
|
|
1623
|
+
input = redactSessionValue(input).value;
|
|
1527
1624
|
const existing = this.db.prepare('SELECT data FROM deletion_receipts WHERE operation_id=?').get(input.preview.operationId);
|
|
1528
1625
|
if (existing)
|
|
1529
1626
|
return JSON.parse(String(existing.data));
|
|
@@ -1543,7 +1640,7 @@ export class SessionRepository {
|
|
|
1543
1640
|
if (!mutationWorkspaceId) {
|
|
1544
1641
|
throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'purge mutation lacks an authorized workspace');
|
|
1545
1642
|
}
|
|
1546
|
-
const apply = this.
|
|
1643
|
+
const apply = this.maintainedAnalyticsTransaction(() => {
|
|
1547
1644
|
for (const decision of validatedDecisions) {
|
|
1548
1645
|
this.db.prepare(`INSERT INTO promotion_dependency_decisions
|
|
1549
1646
|
(operation_id, dependent_id, action, basis) VALUES (?, ?, ?, ?)`).run(input.preview.operationId, decision.dependentId, decision.action, decision.basis);
|
|
@@ -1637,7 +1734,7 @@ export class SessionRepository {
|
|
|
1637
1734
|
return apply();
|
|
1638
1735
|
}
|
|
1639
1736
|
reindex(workspaceId) {
|
|
1640
|
-
const rebuild = this.
|
|
1737
|
+
const rebuild = this.maintainedAnalyticsTransaction(() => {
|
|
1641
1738
|
let indexed = 0;
|
|
1642
1739
|
if (!workspaceId) {
|
|
1643
1740
|
this.db.exec('REINDEX');
|