@lmctl-ai/lmctl 0.1.178 → 0.1.180

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.
@@ -793,3 +793,115 @@ CREATE TRIGGER IF NOT EXISTS trg_agent_mailbox_response_request_update
793
793
  BEGIN
794
794
  SELECT RAISE(ABORT, 'agent_mailbox response requires request_mailbox_id');
795
795
  END;
796
+
797
+ -- v53: event-log message substrate (design:
798
+ -- backlog/DESIGN-event-log-message-substrate-DRAFT.md §2/§4; impl plan:
799
+ -- backlog/IMPL-PLAN-event-log-message-substrate.md Phase A). Additive only —
800
+ -- no existing read/write path changes in this phase.
801
+ --
802
+ -- message_event is the append-only log. `seq` (not `ts`) is the ordering
803
+ -- authority; `ts` is display/audit only. `event_id` is the idempotency key
804
+ -- and MUST be deterministic from intent (hash of stream_id + event_type +
805
+ -- observed projection last_seq — see src/db/event-log.ts), never a random
806
+ -- per-attempt id. The generated sender/receiver columns are load-bearing
807
+ -- (promoted from caveat by the low-level-primitives review): hot selectors
808
+ -- cannot index json_extract() directly; they populate on CREATE/
809
+ -- RESPONSE_CREATE payloads and are NULL for lease/invocation/terminal
810
+ -- streams (json_extract returns NULL on a missing path).
811
+ CREATE TABLE IF NOT EXISTS message_event (
812
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
813
+ event_id TEXT NOT NULL UNIQUE,
814
+ ts TEXT NOT NULL,
815
+ stream_id TEXT NOT NULL,
816
+ event_type TEXT NOT NULL,
817
+ payload TEXT NOT NULL CHECK(json_valid(payload)),
818
+ sender_teamfile TEXT GENERATED ALWAYS AS (json_extract(payload, '$.sender.teamfile')) STORED,
819
+ sender_alias TEXT GENERATED ALWAYS AS (json_extract(payload, '$.sender.alias')) STORED,
820
+ receiver_teamfile TEXT GENERATED ALWAYS AS (json_extract(payload, '$.receiver.teamfile')) STORED,
821
+ receiver_alias TEXT GENERATED ALWAYS AS (json_extract(payload, '$.receiver.alias')) STORED
822
+ );
823
+ CREATE INDEX IF NOT EXISTS idx_message_event_stream ON message_event(stream_id, seq);
824
+ CREATE INDEX IF NOT EXISTS idx_message_event_type ON message_event(event_type, seq);
825
+ CREATE INDEX IF NOT EXISTS idx_message_event_sender ON message_event(sender_teamfile, sender_alias, seq);
826
+ CREATE INDEX IF NOT EXISTS idx_message_event_receiver ON message_event(receiver_teamfile, receiver_alias, seq);
827
+
828
+ -- Mutex + terminal occupancy projection. Will replace agent_inflight + the
829
+ -- terminal-lock slice of runtime_state at cutover (Phase D). PK is the
830
+ -- actual mutual-exclusion constraint. The terminal_invocation_* slice is
831
+ -- dual-written by terminal-kind INVOCATION_START/HEARTBEAT/FINISH events so
832
+ -- ALL THREE busy-check tiers resolve from this one row (the phantom-tolerant
833
+ -- invocation_state below is never read by the busy check).
834
+ CREATE TABLE IF NOT EXISTS agent_state (
835
+ receiver_teamfile TEXT NOT NULL,
836
+ receiver_alias TEXT NOT NULL,
837
+ lease_id TEXT UNIQUE,
838
+ sender_teamfile TEXT,
839
+ sender_alias TEXT,
840
+ holder_pid INTEGER,
841
+ holder_host TEXT,
842
+ started_at TEXT,
843
+ last_heartbeat_at TEXT,
844
+ terminal_lock_id TEXT UNIQUE,
845
+ terminal_invocation_id TEXT UNIQUE,
846
+ terminal_invocation_pid INTEGER,
847
+ terminal_invocation_host TEXT,
848
+ terminal_invocation_heartbeat_at TEXT,
849
+ last_seq INTEGER NOT NULL,
850
+ PRIMARY KEY(receiver_teamfile, receiver_alias)
851
+ );
852
+
853
+ -- One row per undelivered/unhandled message. Will replace the
854
+ -- delivered_at/delivering_at slice of agent_mailbox. in_reply_to's UNIQUE
855
+ -- enforces "one response per request" at the schema level.
856
+ CREATE TABLE IF NOT EXISTS message_pending (
857
+ message_id TEXT PRIMARY KEY,
858
+ message_type TEXT NOT NULL CHECK(message_type IN ('request','response')),
859
+ in_reply_to TEXT UNIQUE,
860
+ sender_teamfile TEXT,
861
+ sender_alias TEXT,
862
+ receiver_teamfile TEXT NOT NULL,
863
+ receiver_alias TEXT NOT NULL,
864
+ lifecycle_state TEXT NOT NULL,
865
+ delivery_lease_id TEXT,
866
+ delivery_holder_pid INTEGER,
867
+ created_seq INTEGER NOT NULL,
868
+ last_seq INTEGER NOT NULL
869
+ );
870
+ CREATE INDEX IF NOT EXISTS idx_message_pending_lane
871
+ ON message_pending(sender_teamfile, sender_alias, receiver_teamfile,
872
+ receiver_alias, message_type, created_seq)
873
+ WHERE lifecycle_state = 'queued';
874
+
875
+ -- Diagnostic/observability projection: "what is a process doing right now."
876
+ -- Deliberately separate from agent_state (two correctness regimes): rows here
877
+ -- are EXPECTED to go stale on hard kills (#88 capture gap) and are cleaned by
878
+ -- a reconciliation probe, never consulted by the busy check.
879
+ CREATE TABLE IF NOT EXISTS invocation_state (
880
+ invocation_id TEXT PRIMARY KEY,
881
+ kind TEXT NOT NULL,
882
+ origin TEXT NOT NULL,
883
+ launch_source TEXT NOT NULL,
884
+ from_identity TEXT NOT NULL,
885
+ target_teamfile TEXT,
886
+ target_alias TEXT,
887
+ root_teamfile TEXT,
888
+ cwd TEXT,
889
+ command TEXT,
890
+ request_message_id TEXT,
891
+ request_payload TEXT,
892
+ holder_pid INTEGER,
893
+ holder_host TEXT,
894
+ status TEXT NOT NULL,
895
+ started_at TEXT NOT NULL,
896
+ last_heartbeat_at TEXT,
897
+ ended_at TEXT,
898
+ result_summary TEXT,
899
+ error TEXT,
900
+ notified_by TEXT,
901
+ notified_at TEXT,
902
+ last_seq INTEGER NOT NULL
903
+ );
904
+ CREATE INDEX IF NOT EXISTS idx_invocation_state_target_status
905
+ ON invocation_state(target_teamfile, target_alias, status, launch_source);
906
+ CREATE INDEX IF NOT EXISTS idx_invocation_state_from_time
907
+ ON invocation_state(from_identity, started_at DESC);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmctl-ai/lmctl",
3
- "version": "0.1.178",
3
+ "version": "0.1.180",
4
4
  "description": "A provider-agnostic control plane for teams of AI coding agents — across providers, with independent review and durable memory.",
5
5
  "type": "module",
6
6
  "homepage": "https://lmctl.com",