loki-mode 10.10.3 → 10.11.0
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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/BOARD.md +8 -0
- package/docs/v10/CP-ASK-PLAN.md +85 -0
- package/docs/v10/DEPS.md +2 -0
- package/docs/v10/FAILURE-CLASSES.md +61 -16
- package/docs/v10/FC21B-FC22-PLAN.md +107 -0
- package/docs/v10/FOUNDER-QUEUE.md +2 -0
- package/docs/v10/METRICS.md +10 -0
- package/docs/v10/PROGRESS.md +23 -0
- package/loki-ts/dist/loki.js +681 -660
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +86 -3
- package/mcp/tests/_read_only_wall_probe.py +156 -0
- package/mcp/tests/test_read_only.py +289 -0
- package/memory/storage.py +41 -3
- package/package.json +1 -1
- package/packages/control-plane/dist/server.js +91 -5
- package/packages/control-plane/drizzle/0005_ask.sql +39 -0
- package/packages/control-plane/drizzle/meta/0005_snapshot.json +800 -0
- package/packages/control-plane/drizzle/meta/_journal.json +7 -0
- package/packages/control-plane/src/ask/parse.ts +120 -0
- package/packages/control-plane/src/db/schema.ts +43 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-Dyx5OHmP.js → AdminPage-qTfCHm93.js} +1 -1
- package/web-app/dist/assets/{Avatar-WVxt-kYj.js → Avatar-V5Wfzf34.js} +1 -1
- package/web-app/dist/assets/{Badge-16vWJ-6u.js → Badge-B7fNko8Z.js} +1 -1
- package/web-app/dist/assets/{Button-CWVPIMTW.js → Button-C_3eInAe.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-D5WhogJK.js → CockpitPage-Ir2wag2q.js} +1 -1
- package/web-app/dist/assets/{ComparePage-CjJfjxcT.js → ComparePage-qbC63b0U.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-CNnObh81.js → ErrorBoundary-BTEqFTCi.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-Cn1PE_JV.js → EvidenceReceiptPanel-CcICm1gj.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-CcH9neSU.js → GitHubIssuesPanel-CXCWYQqX.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-C-n6inCN.js → GitHubPRsPanel-DmzhZYpS.js} +1 -1
- package/web-app/dist/assets/{HomePage-BZ7PCPFR.js → HomePage-BRPspBy7.js} +2 -2
- package/web-app/dist/assets/{LoginPage-CJsigIUa.js → LoginPage-C1Nb7Vf9.js} +1 -1
- package/web-app/dist/assets/{MagicPage-ClZaKmXT.js → MagicPage-CeXc60VN.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-ELQn9-cf.js → MetricsPage-MuDDdqNu.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-Dc_zp3RF.js → NotFoundPage-8iOaj85A.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-B5K1NDuH.js → ProjectPage-DOKcPhxr.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-C2l5SiIN.js → ProjectsPage-CNKaqvuO.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DkDNp6Pd.js → SettingsPage-uZT6dOT1.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-1zasFSKz.js → ShowcasePage-CHzrwMZ3.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-hIm_4NUx.js → SystemSettingsPage-DMHj5Xub.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DUb_TA22.js → TeamsPage-IGY1oZ2o.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-B1mj-eA4.js → TemplatesPage-Byxc8XyZ.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-BX1UwelI.js → TerminalOutput-CRamR4sR.js} +1 -1
- package/web-app/dist/assets/{activity-B8APKMuh.js → activity-DQfL1H8b.js} +1 -1
- package/web-app/dist/assets/{bell-BS8WL-tI.js → bell-CVksERF2.js} +1 -1
- package/web-app/dist/assets/{bot-e9I6yy12.js → bot-jSx_C-35.js} +1 -1
- package/web-app/dist/assets/{check-BbgR7Ovw.js → check-o3bOhMvE.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BcDqv7aX.js → chevron-left-DBhM8Y2E.js} +1 -1
- package/web-app/dist/assets/{circle-alert-BnDT8wxe.js → circle-alert-vCzO_mB8.js} +1 -1
- package/web-app/dist/assets/{clock-LPz79lks.js → clock-Bj125ZaJ.js} +1 -1
- package/web-app/dist/assets/{cloud-BcgBi5T1.js → cloud-DpLr6uQJ.js} +1 -1
- package/web-app/dist/assets/{code-xml-5zcykgva.js → code-xml-VuYrHYvT.js} +1 -1
- package/web-app/dist/assets/{database-D9KLqhHq.js → database-DBmPbwaG.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CrINoZ4o.js → dollar-sign-DEajFwH1.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CA7G72ip.js → file-code-corner-BidTyjV_.js} +1 -1
- package/web-app/dist/assets/{file-plus-D8tHdHn_.js → file-plus-DJ4dSvxw.js} +1 -1
- package/web-app/dist/assets/{globe-CT0Ts5bO.js → globe-BnGEwsOq.js} +1 -1
- package/web-app/dist/assets/{hammer-tUpu0LJk.js → hammer-KCPviXRE.js} +1 -1
- package/web-app/dist/assets/{index-DdjeiWUI.js → index-CdK6NQG3.js} +3 -3
- package/web-app/dist/assets/{layers-BzzJ7w3C.js → layers-7G4hsaDk.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BBdMdxIV.js → loader-circle-CWVxvRmJ.js} +1 -1
- package/web-app/dist/assets/{lock-Cg9DkEh7.js → lock-BEH3dNM0.js} +1 -1
- package/web-app/dist/assets/{package-DHs8IEJR.js → package-DvijfmPC.js} +1 -1
- package/web-app/dist/assets/{plus-70JSAqB1.js → plus-CEwJzXGx.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DjggkboT.js → refresh-cw-Ct7rMsNS.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-c1NNyyL5.js → rotate-ccw-CoTyya_B.js} +1 -1
- package/web-app/dist/assets/{scroll-text-B49wQrJ2.js → scroll-text-CbsOnRXr.js} +1 -1
- package/web-app/dist/assets/{server-BHKG-dDi.js → server-MUPVBbag.js} +1 -1
- package/web-app/dist/assets/{shield-alert-Dx9MoBGm.js → shield-alert-CWwdqRUv.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-frEbS59j.js → thumbs-up-CPVKKyLS.js} +1 -1
- package/web-app/dist/assets/{trash-2-1Gd8pXFG.js → trash-2-BqzCzljk.js} +1 -1
- package/web-app/dist/assets/{trending-down-BpUeO-FP.js → trending-down-DAo5VuPc.js} +1 -1
- package/web-app/dist/assets/{trending-up-CvhWp8qO.js → trending-up-BCUggli9.js} +1 -1
- package/web-app/dist/assets/{upload-DZuMugqd.js → upload-iQijlCY2.js} +1 -1
- package/web-app/dist/assets/{usePolling-D5KUSkfs.js → usePolling-B6kojZR_.js} +1 -1
- package/web-app/dist/assets/{user-BF9vws84.js → user-DR3XkyHZ.js} +1 -1
- package/web-app/dist/index.html +1 -1
package/memory/storage.py
CHANGED
|
@@ -71,6 +71,19 @@ class MemoryStorage:
|
|
|
71
71
|
Supports namespace-based project isolation for memory separation.
|
|
72
72
|
"""
|
|
73
73
|
|
|
74
|
+
# Process-wide read-only switch, set by mcp/server.py apply_read_only_mode.
|
|
75
|
+
# When True, constructing a store creates nothing (no directories, no
|
|
76
|
+
# index/timeline init, no cleanup), reads take no lock file, and any write
|
|
77
|
+
# raises (every public mutator calls _check_writable). A missing store
|
|
78
|
+
# simply reads as empty.
|
|
79
|
+
READ_ONLY = False
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _check_writable() -> None:
|
|
83
|
+
"""Raise PermissionError when the process-wide READ_ONLY flag is set."""
|
|
84
|
+
if MemoryStorage.READ_ONLY:
|
|
85
|
+
raise PermissionError("memory store is read-only in this process")
|
|
86
|
+
|
|
74
87
|
VERSION = "1.1.0"
|
|
75
88
|
|
|
76
89
|
def __init__(
|
|
@@ -112,9 +125,10 @@ class MemoryStorage:
|
|
|
112
125
|
# called on the same path from nested operations in the same thread.
|
|
113
126
|
self._held_locks: threading.local = threading.local()
|
|
114
127
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
if not MemoryStorage.READ_ONLY:
|
|
129
|
+
self._ensure_directories()
|
|
130
|
+
self._ensure_index()
|
|
131
|
+
self._ensure_timeline()
|
|
118
132
|
|
|
119
133
|
@property
|
|
120
134
|
def namespace(self) -> Optional[str]:
|
|
@@ -316,6 +330,11 @@ class MemoryStorage:
|
|
|
316
330
|
Yields:
|
|
317
331
|
File handle with lock held
|
|
318
332
|
"""
|
|
333
|
+
if MemoryStorage.READ_ONLY:
|
|
334
|
+
# No lock file may be created; reads proceed unlocked.
|
|
335
|
+
yield
|
|
336
|
+
return
|
|
337
|
+
|
|
319
338
|
lock_path = path.with_suffix(path.suffix + ".lock")
|
|
320
339
|
lock_key = str(lock_path)
|
|
321
340
|
|
|
@@ -364,6 +383,8 @@ class MemoryStorage:
|
|
|
364
383
|
path: Target file path
|
|
365
384
|
data: Dictionary to serialize as JSON
|
|
366
385
|
"""
|
|
386
|
+
if MemoryStorage.READ_ONLY:
|
|
387
|
+
raise PermissionError("memory store is read-only in this process")
|
|
367
388
|
path = Path(path)
|
|
368
389
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
369
390
|
|
|
@@ -482,6 +503,7 @@ class MemoryStorage:
|
|
|
482
503
|
Returns:
|
|
483
504
|
Episode ID
|
|
484
505
|
"""
|
|
506
|
+
self._check_writable()
|
|
485
507
|
# Handle both dict and object
|
|
486
508
|
if hasattr(episode, "to_dict"):
|
|
487
509
|
episode_data = episode.to_dict()
|
|
@@ -620,6 +642,7 @@ class MemoryStorage:
|
|
|
620
642
|
Returns:
|
|
621
643
|
True if deleted, False if not found
|
|
622
644
|
"""
|
|
645
|
+
self._check_writable()
|
|
623
646
|
episodic_dir = self.base_path / "episodic"
|
|
624
647
|
if not episodic_dir.exists():
|
|
625
648
|
return False
|
|
@@ -690,6 +713,7 @@ class MemoryStorage:
|
|
|
690
713
|
Returns:
|
|
691
714
|
Pattern ID
|
|
692
715
|
"""
|
|
716
|
+
self._check_writable()
|
|
693
717
|
# Handle both dict and object
|
|
694
718
|
if hasattr(pattern, "to_dict"):
|
|
695
719
|
pattern_data = pattern.to_dict()
|
|
@@ -826,6 +850,7 @@ class MemoryStorage:
|
|
|
826
850
|
Returns:
|
|
827
851
|
True if updated, False if not found
|
|
828
852
|
"""
|
|
853
|
+
self._check_writable()
|
|
829
854
|
# Handle both dict and object
|
|
830
855
|
if hasattr(pattern, "to_dict"):
|
|
831
856
|
pattern_data = pattern.to_dict()
|
|
@@ -906,6 +931,7 @@ class MemoryStorage:
|
|
|
906
931
|
True if the pattern was found and the merged record written, False if
|
|
907
932
|
the pattern id was not present (caller should fall back to a create).
|
|
908
933
|
"""
|
|
934
|
+
self._check_writable()
|
|
909
935
|
if not pattern_id:
|
|
910
936
|
return False
|
|
911
937
|
|
|
@@ -968,6 +994,7 @@ class MemoryStorage:
|
|
|
968
994
|
Returns:
|
|
969
995
|
Skill ID
|
|
970
996
|
"""
|
|
997
|
+
self._check_writable()
|
|
971
998
|
# Handle both dict and object
|
|
972
999
|
if hasattr(skill, "to_dict"):
|
|
973
1000
|
skill_data = skill.to_dict()
|
|
@@ -1053,6 +1080,7 @@ class MemoryStorage:
|
|
|
1053
1080
|
|
|
1054
1081
|
Scans all memory stores and builds a topic index for efficient lookup.
|
|
1055
1082
|
"""
|
|
1083
|
+
self._check_writable()
|
|
1056
1084
|
index = {
|
|
1057
1085
|
"version": self.VERSION,
|
|
1058
1086
|
"last_updated": datetime.now(timezone.utc).isoformat(),
|
|
@@ -1126,6 +1154,7 @@ class MemoryStorage:
|
|
|
1126
1154
|
Args:
|
|
1127
1155
|
action: Action dictionary with type, description, timestamp, etc.
|
|
1128
1156
|
"""
|
|
1157
|
+
self._check_writable()
|
|
1129
1158
|
timeline_path = self.base_path / "timeline.json"
|
|
1130
1159
|
|
|
1131
1160
|
with self._file_lock(timeline_path, exclusive=True):
|
|
@@ -1207,6 +1236,7 @@ class MemoryStorage:
|
|
|
1207
1236
|
Args:
|
|
1208
1237
|
context: Dictionary of current context variables
|
|
1209
1238
|
"""
|
|
1239
|
+
self._check_writable()
|
|
1210
1240
|
timeline_path = self.base_path / "timeline.json"
|
|
1211
1241
|
|
|
1212
1242
|
with self._file_lock(timeline_path, exclusive=True):
|
|
@@ -1263,6 +1293,7 @@ class MemoryStorage:
|
|
|
1263
1293
|
|
|
1264
1294
|
def ensure_directory(self, subpath: str) -> None:
|
|
1265
1295
|
"""Create directory if it doesn't exist."""
|
|
1296
|
+
self._check_writable()
|
|
1266
1297
|
path = os.path.join(self.base_path, subpath)
|
|
1267
1298
|
os.makedirs(path, exist_ok=True)
|
|
1268
1299
|
|
|
@@ -1286,6 +1317,7 @@ class MemoryStorage:
|
|
|
1286
1317
|
|
|
1287
1318
|
def write_json(self, filepath: str, data: dict) -> None:
|
|
1288
1319
|
"""Write JSON file atomically."""
|
|
1320
|
+
self._check_writable()
|
|
1289
1321
|
full_path = self._resolve_path(filepath)
|
|
1290
1322
|
self._atomic_write(full_path, data)
|
|
1291
1323
|
|
|
@@ -1298,6 +1330,7 @@ class MemoryStorage:
|
|
|
1298
1330
|
|
|
1299
1331
|
def delete_file(self, filepath: str) -> bool:
|
|
1300
1332
|
"""Delete file, return True if deleted."""
|
|
1333
|
+
self._check_writable()
|
|
1301
1334
|
full_path = self._resolve_path(filepath)
|
|
1302
1335
|
try:
|
|
1303
1336
|
os.remove(full_path)
|
|
@@ -1545,6 +1578,7 @@ class MemoryStorage:
|
|
|
1545
1578
|
Returns:
|
|
1546
1579
|
True if a record was found and persisted, False otherwise.
|
|
1547
1580
|
"""
|
|
1581
|
+
self._check_writable()
|
|
1548
1582
|
memory_id = memory.get("id")
|
|
1549
1583
|
if not memory_id:
|
|
1550
1584
|
return False
|
|
@@ -1656,6 +1690,7 @@ class MemoryStorage:
|
|
|
1656
1690
|
Returns:
|
|
1657
1691
|
True if the pattern was found and incremented, False otherwise.
|
|
1658
1692
|
"""
|
|
1693
|
+
self._check_writable()
|
|
1659
1694
|
patterns_path = self.base_path / "semantic" / "patterns.json"
|
|
1660
1695
|
if not patterns_path.exists():
|
|
1661
1696
|
return False
|
|
@@ -1706,6 +1741,7 @@ class MemoryStorage:
|
|
|
1706
1741
|
Returns:
|
|
1707
1742
|
Number of memories updated
|
|
1708
1743
|
"""
|
|
1744
|
+
self._check_writable()
|
|
1709
1745
|
updated_count = 0
|
|
1710
1746
|
|
|
1711
1747
|
collections_to_process = []
|
|
@@ -1926,6 +1962,7 @@ class MemoryStorage:
|
|
|
1926
1962
|
Returns:
|
|
1927
1963
|
Dictionary with counts of copied items
|
|
1928
1964
|
"""
|
|
1965
|
+
self._check_writable()
|
|
1929
1966
|
target = self.with_namespace(target_namespace)
|
|
1930
1967
|
copied = {"episodes": 0, "patterns": 0, "skills": 0}
|
|
1931
1968
|
|
|
@@ -1970,6 +2007,7 @@ class MemoryStorage:
|
|
|
1970
2007
|
Returns:
|
|
1971
2008
|
Dictionary with counts of merged items
|
|
1972
2009
|
"""
|
|
2010
|
+
self._check_writable()
|
|
1973
2011
|
source = self.with_namespace(source_namespace)
|
|
1974
2012
|
merged = {"episodes": 0, "patterns": 0, "skills": 0}
|
|
1975
2013
|
|
package/package.json
CHANGED
|
@@ -10473,6 +10473,9 @@ class Index {
|
|
|
10473
10473
|
function index(name) {
|
|
10474
10474
|
return new IndexBuilderOn(name, false);
|
|
10475
10475
|
}
|
|
10476
|
+
function uniqueIndex(name) {
|
|
10477
|
+
return new IndexBuilderOn(name, true);
|
|
10478
|
+
}
|
|
10476
10479
|
|
|
10477
10480
|
// node_modules/drizzle-orm/sqlite-core/primary-keys.js
|
|
10478
10481
|
function primaryKey(...config) {
|
|
@@ -12521,6 +12524,9 @@ __export(exports_schema, {
|
|
|
12521
12524
|
localRepos: () => localRepos,
|
|
12522
12525
|
events: () => events,
|
|
12523
12526
|
audit: () => audit2,
|
|
12527
|
+
askThreads: () => askThreads,
|
|
12528
|
+
askMessages: () => askMessages,
|
|
12529
|
+
askEvents: () => askEvents,
|
|
12524
12530
|
actions: () => actions
|
|
12525
12531
|
});
|
|
12526
12532
|
var sources = sqliteTable("sources", {
|
|
@@ -12601,6 +12607,42 @@ var actions = sqliteTable("actions", {
|
|
|
12601
12607
|
result: text("result").notNull(),
|
|
12602
12608
|
detail: text("detail")
|
|
12603
12609
|
}, (t) => [index("actions_ts").on(t.ts)]);
|
|
12610
|
+
var askThreads = sqliteTable("ask_threads", {
|
|
12611
|
+
id: text("id").primaryKey(),
|
|
12612
|
+
createdAt: text("created_at").notNull(),
|
|
12613
|
+
updatedAt: text("updated_at").notNull(),
|
|
12614
|
+
repo: text("repo"),
|
|
12615
|
+
provider: text("provider").notNull(),
|
|
12616
|
+
model: text("model"),
|
|
12617
|
+
title: text("title")
|
|
12618
|
+
});
|
|
12619
|
+
var askMessages = sqliteTable("ask_messages", {
|
|
12620
|
+
id: text("id").primaryKey(),
|
|
12621
|
+
threadId: text("thread_id").notNull().references(() => askThreads.id),
|
|
12622
|
+
seq: integer("seq").notNull(),
|
|
12623
|
+
role: text("role").notNull(),
|
|
12624
|
+
text: text("text").notNull(),
|
|
12625
|
+
status: text("status").notNull(),
|
|
12626
|
+
workerPid: integer("worker_pid"),
|
|
12627
|
+
pgid: integer("pgid"),
|
|
12628
|
+
costUsd: real("cost_usd"),
|
|
12629
|
+
error: text("error"),
|
|
12630
|
+
startedAt: text("started_at"),
|
|
12631
|
+
finishedAt: text("finished_at")
|
|
12632
|
+
}, (t) => [
|
|
12633
|
+
uniqueIndex("ask_messages_thread_seq").on(t.threadId, t.seq),
|
|
12634
|
+
index("ask_messages_status").on(t.status)
|
|
12635
|
+
]);
|
|
12636
|
+
var askEvents = sqliteTable("ask_events", {
|
|
12637
|
+
id: text("id").primaryKey(),
|
|
12638
|
+
messageId: text("message_id").notNull().references(() => askMessages.id),
|
|
12639
|
+
seq: integer("seq").notNull(),
|
|
12640
|
+
kind: text("kind").notNull(),
|
|
12641
|
+
payload: text("payload").notNull(),
|
|
12642
|
+
ts: text("ts").notNull()
|
|
12643
|
+
}, (t) => [
|
|
12644
|
+
uniqueIndex("ask_events_message_seq").on(t.messageId, t.seq)
|
|
12645
|
+
]);
|
|
12604
12646
|
|
|
12605
12647
|
// src/db/migrate.ts
|
|
12606
12648
|
function openDb(path) {
|
|
@@ -17187,6 +17229,21 @@ function checkCommand(repoDir, v, where, errs) {
|
|
|
17187
17229
|
const cwd = checkDir(repoDir, v.cwd, `${where}.cwd`, errs);
|
|
17188
17230
|
return { cmd: v.cmd.trim(), cwd, cite: checkCites(repoDir, v.cite, `${where}.cite`, errs) };
|
|
17189
17231
|
}
|
|
17232
|
+
function rawDeps(v, where, errs) {
|
|
17233
|
+
if (!Array.isArray(v)) {
|
|
17234
|
+
errs.push(`${where}: must be an array of package roots`);
|
|
17235
|
+
return [];
|
|
17236
|
+
}
|
|
17237
|
+
const out2 = [];
|
|
17238
|
+
for (const d of v) {
|
|
17239
|
+
const rel = isStr(d) ? safeRel(d) : null;
|
|
17240
|
+
if (rel === null)
|
|
17241
|
+
errs.push(`${where}: "${String(d)}" is not a repo-relative package root`);
|
|
17242
|
+
else if (!out2.includes(rel))
|
|
17243
|
+
out2.push(rel);
|
|
17244
|
+
}
|
|
17245
|
+
return out2;
|
|
17246
|
+
}
|
|
17190
17247
|
function validateAnswer(repoDir, raw2) {
|
|
17191
17248
|
const errs = [];
|
|
17192
17249
|
if (!isRec(raw2))
|
|
@@ -17219,9 +17276,16 @@ function validateAnswer(repoDir, raw2) {
|
|
|
17219
17276
|
runner: isStr(p.runner) ? p.runner.trim() : null,
|
|
17220
17277
|
commands,
|
|
17221
17278
|
ui: { present: ui.present === true, boot: checkCommand(repoDir, ui.boot, `${w}.ui.boot`, errs), cite: checkCites(repoDir, ui.cite, `${w}.ui.cite`, errs) },
|
|
17222
|
-
cite: checkCites(repoDir, p.cite, `${w}.cite`, errs)
|
|
17279
|
+
cite: checkCites(repoDir, p.cite, `${w}.cite`, errs),
|
|
17280
|
+
...p.install === undefined ? {} : { install: checkCommand(repoDir, p.install, `${w}.install`, errs) },
|
|
17281
|
+
...p.dependsOn === undefined ? {} : { dependsOn: rawDeps(p.dependsOn, `${w}.dependsOn`, errs) }
|
|
17223
17282
|
});
|
|
17224
17283
|
}
|
|
17284
|
+
const roots = new Set(packages.map((p) => p.root));
|
|
17285
|
+
for (const p of packages)
|
|
17286
|
+
for (const d of p.dependsOn ?? [])
|
|
17287
|
+
if (!roots.has(d))
|
|
17288
|
+
errs.push(`package "${p.name}".dependsOn: "${d}" is not the root of any listed package`);
|
|
17225
17289
|
if (!Array.isArray(raw2.fingerprintFiles))
|
|
17226
17290
|
errs.push("fingerprintFiles: required array of the manifest and lockfile paths");
|
|
17227
17291
|
const fingerprintFiles = (Array.isArray(raw2.fingerprintFiles) ? raw2.fingerprintFiles : []).filter((f) => {
|
|
@@ -17305,6 +17369,14 @@ function projectApi(model) {
|
|
|
17305
17369
|
return p ? p.root === "." ? clean(file) : clean(file).slice(p.root.length + 1) : null;
|
|
17306
17370
|
},
|
|
17307
17371
|
commandFor: (pkg, kind) => (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.commands?.[kind] ?? null,
|
|
17372
|
+
dependsOn: (pkg) => {
|
|
17373
|
+
const d = (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.dependsOn;
|
|
17374
|
+
return Array.isArray(d) ? d.filter((x) => typeof x === "string") : null;
|
|
17375
|
+
},
|
|
17376
|
+
installFor: (pkg) => {
|
|
17377
|
+
const i = (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.install;
|
|
17378
|
+
return isRec2(i) && typeof i.cmd === "string" ? i : null;
|
|
17379
|
+
},
|
|
17308
17380
|
hasUI: () => packages.some((p) => isRec2(p.ui) && p.ui.present === true),
|
|
17309
17381
|
uiBoot: () => {
|
|
17310
17382
|
for (const p of packages)
|
|
@@ -17654,6 +17726,13 @@ function receiptScreensProblem(receiptPath, receipt) {
|
|
|
17654
17726
|
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname8(receiptPath), receipt["evidence_screens"]);
|
|
17655
17727
|
}
|
|
17656
17728
|
|
|
17729
|
+
// ../../loki-ts/src/util/check_result.ts
|
|
17730
|
+
var NO_TESTS_REASON = "no tests executed";
|
|
17731
|
+
var UNCONFIRMED_REASON = "test count could not be confirmed";
|
|
17732
|
+
var UNMEASURED_REASON = "executed count unmeasured (Loki could not parse the runner summary, harness-owned)";
|
|
17733
|
+
var isExecutedProof = (c) => c.result === "pass" && typeof c.n === "number" && c.n > 0;
|
|
17734
|
+
var hasExecutedProof = (checks) => checks.some(isExecutedProof);
|
|
17735
|
+
|
|
17657
17736
|
// ../../loki-ts/src/features/contract.ts
|
|
17658
17737
|
import { existsSync as existsSync16, lstatSync as lstatSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync21, writeFileSync as writeFileSync9 } from "fs";
|
|
17659
17738
|
import { dirname as dirname9, join as join28 } from "path";
|
|
@@ -18019,10 +18098,10 @@ var commitStage = {
|
|
|
18019
18098
|
return { status: "completed", data: { committed: true, head_sha: (await git(ctx, ["rev-parse", "HEAD"])).out.trim(), scope_notes: notes } };
|
|
18020
18099
|
}
|
|
18021
18100
|
};
|
|
18022
|
-
function verdictOf2(o, checks, emptyDiff, verifyNotProven, wallGreenOnBase) {
|
|
18101
|
+
function verdictOf2(o, checks, emptyDiff, verifyNotProven, wallGreenOnBase, proof) {
|
|
18023
18102
|
const exit = o.implement?.exit;
|
|
18024
18103
|
if (o.commit?.failed !== true && strs2(o.commit?.not_proven).length === 0 && (o.intake?.already_satisfied === true || wallGreenOnBase || exit === "already_done"))
|
|
18025
|
-
return "ALREADY_SATISFIED";
|
|
18104
|
+
return proof ? "ALREADY_SATISFIED" : "PARTIAL";
|
|
18026
18105
|
if (exit === "spec_conflict")
|
|
18027
18106
|
return "SPEC_CONFLICT";
|
|
18028
18107
|
if (o.commit?.failed === true || strs2(o.commit?.not_proven).length > 0)
|
|
@@ -18031,7 +18110,7 @@ function verdictOf2(o, checks, emptyDiff, verifyNotProven, wallGreenOnBase) {
|
|
|
18031
18110
|
return "FAILED";
|
|
18032
18111
|
if (checks.some((c) => c.result === "fail"))
|
|
18033
18112
|
return "FAILED";
|
|
18034
|
-
if (exit === "killed" || checks.length === 0 || checks.some((c) => c.result !== "pass") || verifyNotProven)
|
|
18113
|
+
if (exit === "killed" || checks.length === 0 || checks.some((c) => c.result !== "pass") || verifyNotProven || !proof)
|
|
18035
18114
|
return "PARTIAL";
|
|
18036
18115
|
return "VERIFIED";
|
|
18037
18116
|
}
|
|
@@ -18089,10 +18168,17 @@ var sealStage = {
|
|
|
18089
18168
|
if ((rawDiff[i].trim().split(" ").pop() ?? "") !== "A" && isTestFile(rawDiff[i + 1]))
|
|
18090
18169
|
weakTests.push(rawDiff[i + 1]);
|
|
18091
18170
|
const grp = sealGroup(ctx.runDir, receiptSha256);
|
|
18092
|
-
const
|
|
18171
|
+
const proof = wallGreenOnBase || hasExecutedProof(Array.isArray(o.verify?.checks) ? o.verify.checks : []);
|
|
18172
|
+
const verdict = capGroupVerdict(verdictOf2(o, checks.filter((c) => !(c.result === "fail" && preRedChecks.includes(c.name))), !diffOk || diff.stdout === "", verifyNotProven.length > 0 || weakTests.length > 0, wallGreenOnBase, proof), grp);
|
|
18093
18173
|
const notProven = new Set([...DEEP_NOT_PROVEN, ...grp.notProven]);
|
|
18174
|
+
if (!proof && (verdict === "PARTIAL" || verdict === "VERIFIED" || verdict === "ALREADY_SATISFIED")) {
|
|
18175
|
+
const vc = Array.isArray(o.verify?.checks) ? o.verify.checks : [];
|
|
18176
|
+
notProven.add(vc.length > 0 && vc.every((c) => c.n !== 0 && String(c.reason ?? "").startsWith(UNMEASURED_REASON)) ? UNMEASURED_REASON : vc.length > 0 && vc.every((c) => c.n !== 0 && String(c.reason ?? "").startsWith(UNCONFIRMED_REASON)) ? UNCONFIRMED_REASON : NO_TESTS_REASON);
|
|
18177
|
+
}
|
|
18094
18178
|
if (wallNotRun > 0)
|
|
18095
18179
|
notProven.add(`wall base run not_run: ${wallNotRun}`);
|
|
18180
|
+
for (const d of Array.isArray(o.wall?.discarded) ? o.wall.discarded : [])
|
|
18181
|
+
notProven.add(`wall test discarded: ${String(d.file)} (${String(d.reason)})`);
|
|
18096
18182
|
if (wallNotRun > 0) {
|
|
18097
18183
|
try {
|
|
18098
18184
|
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename10(String(f.path))));
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS `ask_threads` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`created_at` text NOT NULL,
|
|
4
|
+
`updated_at` text NOT NULL,
|
|
5
|
+
`repo` text,
|
|
6
|
+
`provider` text NOT NULL,
|
|
7
|
+
`model` text,
|
|
8
|
+
`title` text
|
|
9
|
+
);
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
CREATE TABLE IF NOT EXISTS `ask_messages` (
|
|
12
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
13
|
+
`thread_id` text NOT NULL,
|
|
14
|
+
`seq` integer NOT NULL,
|
|
15
|
+
`role` text NOT NULL,
|
|
16
|
+
`text` text NOT NULL,
|
|
17
|
+
`status` text NOT NULL,
|
|
18
|
+
`worker_pid` integer,
|
|
19
|
+
`pgid` integer,
|
|
20
|
+
`cost_usd` real,
|
|
21
|
+
`error` text,
|
|
22
|
+
`started_at` text,
|
|
23
|
+
`finished_at` text,
|
|
24
|
+
FOREIGN KEY (`thread_id`) REFERENCES `ask_threads`(`id`) ON UPDATE no action ON DELETE no action
|
|
25
|
+
);
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
CREATE UNIQUE INDEX IF NOT EXISTS `ask_messages_thread_seq` ON `ask_messages` (`thread_id`,`seq`);--> statement-breakpoint
|
|
28
|
+
CREATE INDEX IF NOT EXISTS `ask_messages_status` ON `ask_messages` (`status`);--> statement-breakpoint
|
|
29
|
+
CREATE TABLE IF NOT EXISTS `ask_events` (
|
|
30
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
31
|
+
`message_id` text NOT NULL,
|
|
32
|
+
`seq` integer NOT NULL,
|
|
33
|
+
`kind` text NOT NULL,
|
|
34
|
+
`payload` text NOT NULL,
|
|
35
|
+
`ts` text NOT NULL,
|
|
36
|
+
FOREIGN KEY (`message_id`) REFERENCES `ask_messages`(`id`) ON UPDATE no action ON DELETE no action
|
|
37
|
+
);
|
|
38
|
+
--> statement-breakpoint
|
|
39
|
+
CREATE UNIQUE INDEX IF NOT EXISTS `ask_events_message_seq` ON `ask_events` (`message_id`,`seq`);
|