salidium 0.1.0 → 0.2.1
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 +55 -108
- package/bundle/salidium.mjs +132 -7
- package/bundle/ui/assets/index-BAFQ-Y4f.js +86 -0
- package/bundle/ui/assets/index-Bob9Rxh_.css +1 -0
- package/bundle/ui/index.html +2 -2
- package/package.json +2 -2
- package/bundle/ui/assets/index-B5glzwgK.css +0 -1
- package/bundle/ui/assets/index-kZRmL4Sh.js +0 -87
package/README.md
CHANGED
|
@@ -1,122 +1,69 @@
|
|
|
1
1
|
# Salidium
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Visualize your agent's work with diagrams and evidence you can check, not walls of text.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- **Verified** — what evidence actually shows (test/build/typecheck/lint runs parsed from real output)
|
|
9
|
-
- **Left** — what remains
|
|
10
|
-
- **Review** — what needs a human (waiting for permission, failing checks, unverified changes, destructive commands, claims without evidence)
|
|
11
|
-
|
|
12
|
-
The daemon, event store, deterministic report, and interface run entirely on your machine with no Salidium telemetry or hosted service. Salidium observes the agent you already run through supported integration points (hooks and the agent's own session logs), derives facts deterministically, and relays the agent's claims verbatim and labelled. Every observed statement links back to the raw record it came from.
|
|
13
|
-
|
|
14
|
-
The optional visual explanation uses your selected installed Claude Code or Codex CLI. Once per completed turn, Salidium sends that agent a bounded, redacted summary of the ask, statements, file names, and check outcomes; the CLI may contact its provider and consume your existing plan or API allowance. The invocation is tool-free, treats the session evidence as untrusted data, and accepts only a bounded, runtime-validated result. Older Codex versions that do not support the tool-disable flags fail closed and simply omit the generated block. Set `SALIDIUM_EXPLAINER=off` (or the legacy `SALIDIUM_EXPLAIN=0`) to send nothing to an agent. The deterministic report still works when explanation is off, unavailable, or fails.
|
|
15
|
-
|
|
16
|
-
It is also allowed to say nothing. A claim the classifier cannot place is recorded and reachable through `record`, and filed under no heading — Salidium may say less than it knows, and may not say something it does not know.
|
|
17
|
-
|
|
18
|
-
The session page is laid out as a written document rather than a dashboard: the product mark, the session title, the facts that identify the run, and the **badges** — did it work, what needs a human, what is unfinished — then a rule, then the **explanation**: one sentence saying what the session is about, followed by the diagrams that carry the meaning. Supporting facts stay behind their toolbar controls so the explanation remains the canvas.
|
|
19
|
-
|
|
20
|
-
Detail is opened by section rather than by one global depth control. **Evidence** opens coverage, checks, changes, and the event record; **Rewind** reconstructs the session at a chosen moment; and **Quantities** and **History** share one supporting inspector beside the page, replacing one another without squeezing the explanation into a third column.
|
|
21
|
-
|
|
22
|
-
The session list groups by the question it answers — **Needs you**, **Working**, **Recent** — because you are usually running more than one agent at a time. It folds away with `[`, and the sessions that started and ended without running a turn are collapsed behind one line. Light, dark and system themes are all first-class and the choice is remembered.
|
|
23
|
-
|
|
24
|
-
Providers today: **Claude Code** (hooks + transcript, primary) and **Codex** (rollout files + hooks).
|
|
25
|
-
|
|
26
|
-
## Run it
|
|
27
|
-
|
|
28
|
-
Requirements: Node ≥ 24, because the daemon uses the built-in `node:sqlite`.
|
|
5
|
+
Reading back a Claude Code or Codex run means scrolling the whole transcript. Salidium reads it and
|
|
6
|
+
draws what happened. You do not have to wait for the run to end. The report updates while the agent
|
|
7
|
+
is still working, and an explanation is written at the end of each turn.
|
|
29
8
|
|
|
30
9
|
```bash
|
|
31
10
|
npx salidium
|
|
32
11
|
```
|
|
33
12
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
salidium
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
salidium
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
13
|
+
<picture>
|
|
14
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twinkling-reality/salidium/main/apps/site/public/tour-dark.gif">
|
|
15
|
+
<img src="https://raw.githubusercontent.com/twinkling-reality/salidium/main/apps/site/public/tour-light.gif" alt="Salidium in use: a report of a checkout fix, the session list folded away to give the report the window, the Evidence panel showing which changed files have had a passing check, the record behind one of them, and the report wound back to a moment when the check was still green.">
|
|
16
|
+
</picture>
|
|
17
|
+
|
|
18
|
+
Salidium is [MIT-licensed](LICENSE), open source, and local-first. The report, event store, and
|
|
19
|
+
interface run on your machine with no Salidium telemetry. Raw transcripts stay local. Optional
|
|
20
|
+
generated explanations send a bounded, redacted excerpt through your installed agent CLI, which
|
|
21
|
+
may contact its provider; they can be turned off.
|
|
22
|
+
|
|
23
|
+
<picture>
|
|
24
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twinkling-reality/salidium/main/apps/site/public/report-dark.png">
|
|
25
|
+
<img src="https://raw.githubusercontent.com/twinkling-reality/salidium/main/apps/site/public/report-light.png" alt="A full report: the verdict, Why drawn as two paths converging on one order charged twice, How drawn as one idempotency key per order, and the approach the agent abandoned beside the one it adopted.">
|
|
26
|
+
</picture>
|
|
27
|
+
|
|
28
|
+
Supported agents: Claude Code and Codex. Node.js 24 or newer is required. On first run Salidium
|
|
29
|
+
finds the agents, shows the local settings it wants to update, asks permission, and opens the
|
|
30
|
+
interface.
|
|
31
|
+
|
|
32
|
+
## Documentation
|
|
33
|
+
|
|
34
|
+
The documentation is at [salidium.com/docs](https://salidium.com/docs).
|
|
35
|
+
|
|
36
|
+
1. [Install](https://salidium.com/docs/install)
|
|
37
|
+
2. [Opening the page](https://salidium.com/docs/the-page)
|
|
38
|
+
3. [Sessions](https://salidium.com/docs/sessions)
|
|
39
|
+
4. [Reading a report](https://salidium.com/docs/report)
|
|
40
|
+
5. [Evidence](https://salidium.com/docs/evidence)
|
|
41
|
+
6. [Rewind, History and Quantities](https://salidium.com/docs/rewind)
|
|
42
|
+
7. [Records](https://salidium.com/docs/records)
|
|
43
|
+
8. [How we know](https://salidium.com/docs/provenance)
|
|
44
|
+
9. [Explanations](https://salidium.com/docs/explanations)
|
|
45
|
+
10. [What stays on your machine](https://salidium.com/docs/local)
|
|
46
|
+
11. [Keyboard](https://salidium.com/docs/keyboard)
|
|
47
|
+
12. [CLI](https://salidium.com/docs/cli)
|
|
48
|
+
13. [Environment](https://salidium.com/docs/environment)
|
|
49
|
+
14. [Limits](https://salidium.com/docs/limits)
|
|
50
|
+
|
|
51
|
+
In this repository: [Using Salidium](docs/using-salidium.md),
|
|
52
|
+
[architecture](docs/architecture.md), the
|
|
53
|
+
[open-source and hosted-service boundary](docs/open-source-boundary.md),
|
|
54
|
+
[long-term intelligence](docs/long-term-intelligence.md) and its
|
|
55
|
+
[evaluation protocol](docs/evaluation-protocol.md), [contributing](CONTRIBUTING.md), and the
|
|
56
|
+
[security policy](SECURITY.md).
|
|
61
57
|
|
|
62
58
|
## Development
|
|
63
59
|
|
|
64
60
|
```bash
|
|
65
|
-
pnpm install
|
|
66
|
-
pnpm
|
|
67
|
-
pnpm
|
|
68
|
-
pnpm
|
|
69
|
-
pnpm
|
|
70
|
-
pnpm format # biome format --write
|
|
71
|
-
pnpm test # vitest: unit, adapter fixtures, daemon integration, performance
|
|
72
|
-
pnpm test:e2e # real-daemon Chromium + automated accessibility checks
|
|
73
|
-
pnpm test:e2e:full # Chromium, Firefox, and WebKit portability matrix
|
|
74
|
-
pnpm package # build the publishable bundle into packages/cli/bundle
|
|
75
|
-
pnpm --filter @salidium/ui dev # Vite dev server for the UI, proxied to the running daemon (open it with #token=… from ~/.salidium/daemon.json)
|
|
61
|
+
pnpm install --frozen-lockfile
|
|
62
|
+
pnpm lint
|
|
63
|
+
pnpm typecheck
|
|
64
|
+
pnpm test
|
|
65
|
+
pnpm build
|
|
76
66
|
```
|
|
77
67
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
starts the daemon from it — because the published bundle and the checkout resolve paths
|
|
81
|
-
differently, and only the second arrangement is what a user gets.
|
|
82
|
-
|
|
83
|
-
Release preparation, the manual-only publish workflow, and rollback steps are documented in
|
|
84
|
-
[`docs/releasing.md`](docs/releasing.md).
|
|
85
|
-
|
|
86
|
-
### Measuring the claims layer
|
|
87
|
-
|
|
88
|
-
`salidium audit-claims` replays every agent message in your store through the classifier and reports what it did with them: the distribution by kind and by rule, how many messages produced nothing at all, and a seeded random sample of each rule's output to read. Classifier behavior can vary with providers and writing styles, so the honest way to know whether its conservative rules hold for your sessions is to measure your own local corpus.
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
salidium audit-claims # counts, plus 8 sampled claims per rule
|
|
92
|
-
salidium audit-claims --only=discovery --sample=20
|
|
93
|
-
salidium audit-claims --json # for a script
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
`pnpm salidium …` runs the CLI from source with Node's type stripping (`--conditions=development`); `pnpm build` emits `dist/` for every package.
|
|
97
|
-
|
|
98
|
-
## Architecture (short)
|
|
99
|
-
|
|
100
|
-
```
|
|
101
|
-
agent runtime ──hooks (async sh relay)──▶ ┐
|
|
102
|
-
──session log (tailed)────▶ ├─▶ provider adapter → canonical events → dedupe → redact → reducer → semantic state + change log
|
|
103
|
-
┘ │
|
|
104
|
-
SQLite (events, changes, checkpoints) ◀─┘─▶ SSE → UI (same reducer)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
- `packages/protocol` — zod schemas shared by everything: canonical events, semantic changes, wire protocol, provenance vocabulary.
|
|
108
|
-
- `packages/core` — pure derivation: `applyEvent(state, event) → SemanticChange[]`, session projections, verification parsing, review rules, redaction, replay.
|
|
109
|
-
- `packages/adapter-kit`, `packages/adapters/*` — provider parsers (transcript/rollout lines + hook payloads → events with deterministic ids).
|
|
110
|
-
- `packages/daemon` — tailing, hook ingress, persistence, git snapshots, loopback HTTP/SSE server.
|
|
111
|
-
- `packages/ui` — React client that folds the event stream with the same reducer.
|
|
112
|
-
- `packages/cli` — `salidium` command.
|
|
113
|
-
|
|
114
|
-
See [docs/architecture.md](docs/architecture.md) for the decisions and boundaries.
|
|
115
|
-
|
|
116
|
-
## Status
|
|
117
|
-
|
|
118
|
-
MVP. The integration and replay paths are exercised against synthetic fixtures and local provider
|
|
119
|
-
sessions for Claude Code and Codex. The claim classifier is deliberately conservative, but its
|
|
120
|
-
behavior may vary across providers and writing styles; `audit-claims` is how to evaluate it on your
|
|
121
|
-
own store. Native Windows history import is supported, but live hooks still require POSIX `sh` and
|
|
122
|
-
`curl`. See the architecture document for the complete limitations.
|
|
68
|
+
Run from source with `pnpm salidium`. The public CLI is bundled as one self-contained script, and
|
|
69
|
+
the separately releasable `@salidium/sync-contract` contains only strict interoperability schemas.
|
package/bundle/salidium.mjs
CHANGED
|
@@ -1114,7 +1114,8 @@ function applyVerificationReview(state, event, log) {
|
|
|
1114
1114
|
epistemic: v.outcomeEpistemic
|
|
1115
1115
|
});
|
|
1116
1116
|
} else if (v.outcome === "pass" && v.scope !== "partial") {
|
|
1117
|
-
|
|
1117
|
+
const covers = v.method !== "lint";
|
|
1118
|
+
resolve(state, event, (r) => r.id === `verification-failed:${v.method}` || covers && r.rule === "changes-unverified");
|
|
1118
1119
|
}
|
|
1119
1120
|
}
|
|
1120
1121
|
function applyReviewRulesAfterEvent(state, event, log) {
|
|
@@ -23044,6 +23045,7 @@ var SessionCoordinator = class _SessionCoordinator {
|
|
|
23044
23045
|
cadence: envAllows ? "turn" : "off",
|
|
23045
23046
|
idleEndMs: IDLE_END_MS,
|
|
23046
23047
|
explainSession: explainWithStatus,
|
|
23048
|
+
now: Date.now,
|
|
23047
23049
|
...args.options
|
|
23048
23050
|
};
|
|
23049
23051
|
const cp = store.latestCheckpoint(sessionId, REDUCER_VERSION);
|
|
@@ -23083,7 +23085,7 @@ var SessionCoordinator = class _SessionCoordinator {
|
|
|
23083
23085
|
return coord;
|
|
23084
23086
|
}
|
|
23085
23087
|
get summary() {
|
|
23086
|
-
const s = summarizeSession(this.state,
|
|
23088
|
+
const s = summarizeSession(this.state, this.opts.now());
|
|
23087
23089
|
s.explanationStatus = this.explanationIsCurrent() ? "generated" : this.explanationStatus;
|
|
23088
23090
|
return s;
|
|
23089
23091
|
}
|
|
@@ -23397,10 +23399,13 @@ var SessionRegistry = class {
|
|
|
23397
23399
|
* load: it is one value for the whole daemon, and a coordinator is created on the ingest path.
|
|
23398
23400
|
*/
|
|
23399
23401
|
explainerCadence = "turn";
|
|
23402
|
+
/** Handed to every coordinator this registry loads; see `CoordinatorOptions.now`. */
|
|
23403
|
+
now;
|
|
23400
23404
|
constructor(store, opts = {}) {
|
|
23401
23405
|
this.store = store;
|
|
23402
23406
|
if (opts.explainerCadence)
|
|
23403
23407
|
this.explainerCadence = opts.explainerCadence;
|
|
23408
|
+
this.now = opts.now;
|
|
23404
23409
|
this.listener = {
|
|
23405
23410
|
onEvents: (sessionId, events, changes) => {
|
|
23406
23411
|
for (const s of this.allSubscribers) {
|
|
@@ -23444,7 +23449,7 @@ var SessionRegistry = class {
|
|
|
23444
23449
|
cwd: hint?.cwd,
|
|
23445
23450
|
store: this.store,
|
|
23446
23451
|
listener: this.listener,
|
|
23447
|
-
options: { cadence: this.explainerCadence }
|
|
23452
|
+
options: { cadence: this.explainerCadence, ...this.now ? { now: this.now } : {} }
|
|
23448
23453
|
});
|
|
23449
23454
|
c.onError = (err) => this.onPersistError?.(sessionId, err);
|
|
23450
23455
|
this.live.set(sessionId, c);
|
|
@@ -24169,7 +24174,7 @@ import { dirname as dirname2 } from "node:path";
|
|
|
24169
24174
|
import { DatabaseSync } from "node:sqlite";
|
|
24170
24175
|
import { isDeepStrictEqual } from "node:util";
|
|
24171
24176
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
24172
|
-
var SCHEMA_VERSION =
|
|
24177
|
+
var SCHEMA_VERSION = 6;
|
|
24173
24178
|
var INGEST_PARSER_REVISION = "2026-08-19.1";
|
|
24174
24179
|
function existingSchemaVersion(db) {
|
|
24175
24180
|
try {
|
|
@@ -24312,6 +24317,109 @@ CREATE TABLE IF NOT EXISTS usage_rollups (
|
|
|
24312
24317
|
cache_write_tokens INTEGER NOT NULL DEFAULT 0
|
|
24313
24318
|
);
|
|
24314
24319
|
`;
|
|
24320
|
+
var SCHEMA_6_DDL = `
|
|
24321
|
+
CREATE TABLE IF NOT EXISTS sync_identity (
|
|
24322
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
24323
|
+
replica_id TEXT NOT NULL UNIQUE,
|
|
24324
|
+
created_at TEXT NOT NULL
|
|
24325
|
+
);
|
|
24326
|
+
CREATE TABLE IF NOT EXISTS sync_streams (
|
|
24327
|
+
stream_id TEXT PRIMARY KEY,
|
|
24328
|
+
replica_id TEXT NOT NULL,
|
|
24329
|
+
-- Positions are unique per generation, not per replica. A replica restored from an older backup
|
|
24330
|
+
-- bumps this so its reused positions are a declared restart rather than a content conflict.
|
|
24331
|
+
replica_generation INTEGER NOT NULL DEFAULT 1,
|
|
24332
|
+
destination_id TEXT NOT NULL UNIQUE,
|
|
24333
|
+
next_control_position INTEGER NOT NULL DEFAULT 0,
|
|
24334
|
+
next_data_position INTEGER NOT NULL DEFAULT 0,
|
|
24335
|
+
acknowledged_control_position INTEGER NOT NULL DEFAULT -1,
|
|
24336
|
+
acknowledged_data_position INTEGER NOT NULL DEFAULT -1,
|
|
24337
|
+
previous_control_operation_id TEXT,
|
|
24338
|
+
previous_data_operation_id TEXT,
|
|
24339
|
+
created_at TEXT NOT NULL
|
|
24340
|
+
);
|
|
24341
|
+
CREATE TABLE IF NOT EXISTS sync_consent_revisions (
|
|
24342
|
+
stream_id TEXT NOT NULL,
|
|
24343
|
+
grant_id TEXT NOT NULL,
|
|
24344
|
+
revision INTEGER NOT NULL,
|
|
24345
|
+
status TEXT NOT NULL CHECK (status IN ('active', 'revoked')),
|
|
24346
|
+
destination_id TEXT NOT NULL,
|
|
24347
|
+
json TEXT NOT NULL,
|
|
24348
|
+
recorded_at TEXT NOT NULL,
|
|
24349
|
+
PRIMARY KEY (stream_id, grant_id, revision),
|
|
24350
|
+
FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
|
|
24351
|
+
);
|
|
24352
|
+
CREATE INDEX IF NOT EXISTS sync_consent_current
|
|
24353
|
+
ON sync_consent_revisions(stream_id, grant_id, revision DESC);
|
|
24354
|
+
CREATE TABLE IF NOT EXISTS intelligence_records (
|
|
24355
|
+
stream_id TEXT NOT NULL,
|
|
24356
|
+
item_id TEXT NOT NULL,
|
|
24357
|
+
revision INTEGER NOT NULL,
|
|
24358
|
+
kind TEXT NOT NULL,
|
|
24359
|
+
grant_id TEXT NOT NULL,
|
|
24360
|
+
grant_revision INTEGER NOT NULL,
|
|
24361
|
+
current INTEGER NOT NULL CHECK (current IN (0, 1)),
|
|
24362
|
+
json TEXT NOT NULL,
|
|
24363
|
+
recorded_at TEXT NOT NULL,
|
|
24364
|
+
PRIMARY KEY (stream_id, item_id, revision),
|
|
24365
|
+
FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
|
|
24366
|
+
);
|
|
24367
|
+
CREATE UNIQUE INDEX IF NOT EXISTS intelligence_one_current_revision
|
|
24368
|
+
ON intelligence_records(stream_id, item_id) WHERE current = 1;
|
|
24369
|
+
CREATE TABLE IF NOT EXISTS intelligence_evidence_map (
|
|
24370
|
+
evidence_id TEXT PRIMARY KEY,
|
|
24371
|
+
independence_id TEXT NOT NULL,
|
|
24372
|
+
session_id TEXT,
|
|
24373
|
+
event_id TEXT,
|
|
24374
|
+
authority TEXT NOT NULL,
|
|
24375
|
+
role TEXT NOT NULL,
|
|
24376
|
+
captured_at TEXT NOT NULL,
|
|
24377
|
+
descriptor_digest TEXT NOT NULL,
|
|
24378
|
+
created_at TEXT NOT NULL
|
|
24379
|
+
);
|
|
24380
|
+
CREATE TABLE IF NOT EXISTS intelligence_tombstones (
|
|
24381
|
+
stream_id TEXT NOT NULL,
|
|
24382
|
+
item_id TEXT NOT NULL,
|
|
24383
|
+
delete_through_revision INTEGER NOT NULL,
|
|
24384
|
+
reason TEXT NOT NULL,
|
|
24385
|
+
requested_at TEXT NOT NULL,
|
|
24386
|
+
receipt_id TEXT,
|
|
24387
|
+
completed_at TEXT,
|
|
24388
|
+
PRIMARY KEY (stream_id, item_id),
|
|
24389
|
+
FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
|
|
24390
|
+
);
|
|
24391
|
+
CREATE TABLE IF NOT EXISTS sync_outbox (
|
|
24392
|
+
stream_id TEXT NOT NULL,
|
|
24393
|
+
lane TEXT NOT NULL CHECK (lane IN ('control', 'data')),
|
|
24394
|
+
position INTEGER NOT NULL,
|
|
24395
|
+
operation_id TEXT NOT NULL UNIQUE,
|
|
24396
|
+
operation_type TEXT NOT NULL,
|
|
24397
|
+
content_digest TEXT NOT NULL,
|
|
24398
|
+
grant_id TEXT,
|
|
24399
|
+
item_id TEXT,
|
|
24400
|
+
json TEXT NOT NULL,
|
|
24401
|
+
created_at TEXT NOT NULL,
|
|
24402
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
24403
|
+
next_attempt_at TEXT,
|
|
24404
|
+
acknowledged_at TEXT,
|
|
24405
|
+
-- A destination can refuse an operation permanently. That is a distinct outcome from durable
|
|
24406
|
+
-- acceptance and must never be recorded as one, or a refused record would read as delivered.
|
|
24407
|
+
refused_at TEXT,
|
|
24408
|
+
refusal_code TEXT,
|
|
24409
|
+
PRIMARY KEY (stream_id, lane, position),
|
|
24410
|
+
FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
|
|
24411
|
+
);
|
|
24412
|
+
CREATE INDEX IF NOT EXISTS sync_outbox_pending
|
|
24413
|
+
ON sync_outbox(stream_id, lane, acknowledged_at, position);
|
|
24414
|
+
CREATE TABLE IF NOT EXISTS sync_deletion_receipts (
|
|
24415
|
+
receipt_id TEXT PRIMARY KEY,
|
|
24416
|
+
stream_id TEXT NOT NULL,
|
|
24417
|
+
operation_id TEXT NOT NULL,
|
|
24418
|
+
json TEXT NOT NULL,
|
|
24419
|
+
completed_at TEXT NOT NULL,
|
|
24420
|
+
FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
|
|
24421
|
+
);
|
|
24422
|
+
`;
|
|
24315
24423
|
function canonicalizeTimestamp(value) {
|
|
24316
24424
|
if (typeof value !== "string" || value.length === 0)
|
|
24317
24425
|
return void 0;
|
|
@@ -24579,6 +24687,10 @@ function migrateToSchema5(db, migratedAt = (/* @__PURE__ */ new Date()).toISOStr
|
|
|
24579
24687
|
enqueueMissing.run(source.path, source.sessionId, source.provider, source.agentId ?? null, INGEST_PARSER_REVISION, migratedAt);
|
|
24580
24688
|
db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run("schema_5_migrated_at", migratedAt);
|
|
24581
24689
|
}
|
|
24690
|
+
function migrateToSchema6(db, migratedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
24691
|
+
db.exec(SCHEMA_6_DDL);
|
|
24692
|
+
db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run("schema_6_migrated_at", migratedAt);
|
|
24693
|
+
}
|
|
24582
24694
|
var SESSION_PAGE = 500;
|
|
24583
24695
|
var REAL_SESSION = `COALESCE(json_extract(summary_json, '$.internal'), 0) = 0
|
|
24584
24696
|
AND instr(COALESCE(title, ''), '[salidium-explainer]') = 0`;
|
|
@@ -24632,6 +24744,8 @@ var SqliteStore = class {
|
|
|
24632
24744
|
}
|
|
24633
24745
|
this.db.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA temp_store = MEMORY; PRAGMA foreign_keys = ON;");
|
|
24634
24746
|
this.db.exec(DDL);
|
|
24747
|
+
if (priorVersion === void 0 && !hadLegacyTables)
|
|
24748
|
+
this.db.exec(SCHEMA_6_DDL);
|
|
24635
24749
|
try {
|
|
24636
24750
|
this.db.exec("ALTER TABLE changes ADD COLUMN reducer_version TEXT");
|
|
24637
24751
|
} catch {
|
|
@@ -24647,6 +24761,8 @@ var SqliteStore = class {
|
|
|
24647
24761
|
migrateToSchema4(this.db);
|
|
24648
24762
|
if ((priorVersion ?? 0) < 5)
|
|
24649
24763
|
migrateToSchema5(this.db);
|
|
24764
|
+
if ((priorVersion ?? 0) < 6)
|
|
24765
|
+
migrateToSchema6(this.db);
|
|
24650
24766
|
this.db.exec("COMMIT");
|
|
24651
24767
|
} catch (error51) {
|
|
24652
24768
|
try {
|
|
@@ -25336,7 +25452,8 @@ async function startDaemon(overrides = {}) {
|
|
|
25336
25452
|
const stored = readSettings(config2.home, (reason) => log.warn("settings invalid; optional explanations disabled", { reason }));
|
|
25337
25453
|
const envOff = configuredExplainerMode(process.env) === "off";
|
|
25338
25454
|
const registry2 = new SessionRegistry(store, {
|
|
25339
|
-
explainerCadence: effectiveCadence(stored.explainerCadence)
|
|
25455
|
+
explainerCadence: effectiveCadence(stored.explainerCadence),
|
|
25456
|
+
...overrides.now ? { now: overrides.now } : {}
|
|
25340
25457
|
});
|
|
25341
25458
|
const explainerSettings = () => {
|
|
25342
25459
|
const usage = registry2.explainerUsage();
|
|
@@ -25937,7 +26054,11 @@ function createIntegration(definition) {
|
|
|
25937
26054
|
];
|
|
25938
26055
|
}
|
|
25939
26056
|
if (inspection.status === "configured") {
|
|
25940
|
-
|
|
26057
|
+
const ok = {
|
|
26058
|
+
level: "ok",
|
|
26059
|
+
message: `${definition.name} connection is configured`
|
|
26060
|
+
};
|
|
26061
|
+
return definition.standingNote ? [ok, { level: "info", message: definition.standingNote }] : [ok];
|
|
25941
26062
|
}
|
|
25942
26063
|
if (inspection.status === "partial") {
|
|
25943
26064
|
return [
|
|
@@ -25978,6 +26099,7 @@ var codex = createIntegration({
|
|
|
25978
26099
|
id: "codex",
|
|
25979
26100
|
name: "Codex",
|
|
25980
26101
|
command: "codex",
|
|
26102
|
+
standingNote: "Codex honours a hook only once it has been trusted there: open /hooks in Codex. Salidium cannot tell whether that has been done",
|
|
25981
26103
|
stateDirectory(context) {
|
|
25982
26104
|
return environment(context).CODEX_HOME ?? join11(context.userHome, ".codex");
|
|
25983
26105
|
},
|
|
@@ -26300,7 +26422,7 @@ function providerDisplayName(provider, providers = []) {
|
|
|
26300
26422
|
}
|
|
26301
26423
|
|
|
26302
26424
|
// src/main.ts
|
|
26303
|
-
var HELP = `salidium
|
|
26425
|
+
var HELP = `salidium: see what your coding agent did, why, how, what is verified, what is left, and what it flagged for a human.
|
|
26304
26426
|
|
|
26305
26427
|
Usage:
|
|
26306
26428
|
salidium Connect detected agents on first run, then start and open Salidium
|
|
@@ -26570,6 +26692,9 @@ ${uiUrl(running)}
|
|
|
26570
26692
|
for (const validation of provider.validate(context)) {
|
|
26571
26693
|
if (validation.level === "attention")
|
|
26572
26694
|
process.stdout.write(` needs attention: ${validation.message}
|
|
26695
|
+
`);
|
|
26696
|
+
else if (validation.level === "info")
|
|
26697
|
+
process.stdout.write(` note: ${validation.message}
|
|
26573
26698
|
`);
|
|
26574
26699
|
}
|
|
26575
26700
|
}
|