lingxios 1.0.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.
Files changed (298) hide show
  1. package/README.md +54 -0
  2. package/db/schema.sql +306 -0
  3. package/dist/src/app/artifacts.d.ts +8 -0
  4. package/dist/src/app/artifacts.js +114 -0
  5. package/dist/src/app/artifacts.js.map +1 -0
  6. package/dist/src/app/doctor.d.ts +22 -0
  7. package/dist/src/app/doctor.js +44 -0
  8. package/dist/src/app/doctor.js.map +1 -0
  9. package/dist/src/app/index.d.ts +123 -0
  10. package/dist/src/app/index.js +291 -0
  11. package/dist/src/app/index.js.map +1 -0
  12. package/dist/src/app/input.d.ts +22 -0
  13. package/dist/src/app/input.js +60 -0
  14. package/dist/src/app/input.js.map +1 -0
  15. package/dist/src/app/recover-wait.d.ts +5 -0
  16. package/dist/src/app/recover-wait.js +59 -0
  17. package/dist/src/app/recover-wait.js.map +1 -0
  18. package/dist/src/app/resources.d.ts +5 -0
  19. package/dist/src/app/resources.js +9 -0
  20. package/dist/src/app/resources.js.map +1 -0
  21. package/dist/src/app/storage.d.ts +2 -0
  22. package/dist/src/app/storage.js +41 -0
  23. package/dist/src/app/storage.js.map +1 -0
  24. package/dist/src/cli/doctor.d.ts +2 -0
  25. package/dist/src/cli/doctor.js +20 -0
  26. package/dist/src/cli/doctor.js.map +1 -0
  27. package/dist/src/cli/eval.d.ts +2 -0
  28. package/dist/src/cli/eval.js +44 -0
  29. package/dist/src/cli/eval.js.map +1 -0
  30. package/dist/src/config.d.ts +22 -0
  31. package/dist/src/config.js +56 -0
  32. package/dist/src/config.js.map +1 -0
  33. package/dist/src/context/attachments.d.ts +10 -0
  34. package/dist/src/context/attachments.js +18 -0
  35. package/dist/src/context/attachments.js.map +1 -0
  36. package/dist/src/context/document-text.d.ts +1 -0
  37. package/dist/src/context/document-text.js +29 -0
  38. package/dist/src/context/document-text.js.map +1 -0
  39. package/dist/src/context/document-worker.d.ts +1 -0
  40. package/dist/src/context/document-worker.js +62 -0
  41. package/dist/src/context/document-worker.js.map +1 -0
  42. package/dist/src/context/evidence.d.ts +19 -0
  43. package/dist/src/context/evidence.js +37 -0
  44. package/dist/src/context/evidence.js.map +1 -0
  45. package/dist/src/context/request.d.ts +21 -0
  46. package/dist/src/context/request.js +32 -0
  47. package/dist/src/context/request.js.map +1 -0
  48. package/dist/src/context/research-evidence.d.ts +4 -0
  49. package/dist/src/context/research-evidence.js +24 -0
  50. package/dist/src/context/research-evidence.js.map +1 -0
  51. package/dist/src/context/resource-checks.d.ts +7 -0
  52. package/dist/src/context/resource-checks.js +15 -0
  53. package/dist/src/context/resource-checks.js.map +1 -0
  54. package/dist/src/context/task-contract.d.ts +11 -0
  55. package/dist/src/context/task-contract.js +23 -0
  56. package/dist/src/context/task-contract.js.map +1 -0
  57. package/dist/src/context/text.d.ts +2 -0
  58. package/dist/src/context/text.js +7 -0
  59. package/dist/src/context/text.js.map +1 -0
  60. package/dist/src/control-plane/http-server.d.ts +22 -0
  61. package/dist/src/control-plane/http-server.js +196 -0
  62. package/dist/src/control-plane/http-server.js.map +1 -0
  63. package/dist/src/control-plane/memory-store.d.ts +71 -0
  64. package/dist/src/control-plane/memory-store.js +389 -0
  65. package/dist/src/control-plane/memory-store.js.map +1 -0
  66. package/dist/src/control-plane/pg-store.d.ts +62 -0
  67. package/dist/src/control-plane/pg-store.js +386 -0
  68. package/dist/src/control-plane/pg-store.js.map +1 -0
  69. package/dist/src/control-plane/service.d.ts +52 -0
  70. package/dist/src/control-plane/service.js +630 -0
  71. package/dist/src/control-plane/service.js.map +1 -0
  72. package/dist/src/control-plane/stores.d.ts +184 -0
  73. package/dist/src/control-plane/stores.js +19 -0
  74. package/dist/src/control-plane/stores.js.map +1 -0
  75. package/dist/src/errors.d.ts +66 -0
  76. package/dist/src/errors.js +102 -0
  77. package/dist/src/errors.js.map +1 -0
  78. package/dist/src/eval/calibration.d.ts +16 -0
  79. package/dist/src/eval/calibration.js +28 -0
  80. package/dist/src/eval/calibration.js.map +1 -0
  81. package/dist/src/eval/index.d.ts +40 -0
  82. package/dist/src/eval/index.js +40 -0
  83. package/dist/src/eval/index.js.map +1 -0
  84. package/dist/src/eval/review.d.ts +20 -0
  85. package/dist/src/eval/review.js +49 -0
  86. package/dist/src/eval/review.js.map +1 -0
  87. package/dist/src/host/http-client.d.ts +48 -0
  88. package/dist/src/host/http-client.js +127 -0
  89. package/dist/src/host/http-client.js.map +1 -0
  90. package/dist/src/host/port.d.ts +26 -0
  91. package/dist/src/host/port.js +2 -0
  92. package/dist/src/host/port.js.map +1 -0
  93. package/dist/src/index.d.ts +17 -0
  94. package/dist/src/index.js +9 -0
  95. package/dist/src/index.js.map +1 -0
  96. package/dist/src/integrations/lingxiloop/actions.d.ts +25 -0
  97. package/dist/src/integrations/lingxiloop/actions.js +77 -0
  98. package/dist/src/integrations/lingxiloop/actions.js.map +1 -0
  99. package/dist/src/integrations/lingxiloop/app.d.ts +167 -0
  100. package/dist/src/integrations/lingxiloop/app.js +434 -0
  101. package/dist/src/integrations/lingxiloop/app.js.map +1 -0
  102. package/dist/src/integrations/lingxiloop/approvals.d.ts +148 -0
  103. package/dist/src/integrations/lingxiloop/approvals.js +540 -0
  104. package/dist/src/integrations/lingxiloop/approvals.js.map +1 -0
  105. package/dist/src/integrations/lingxiloop/attachments.d.ts +8 -0
  106. package/dist/src/integrations/lingxiloop/attachments.js +38 -0
  107. package/dist/src/integrations/lingxiloop/attachments.js.map +1 -0
  108. package/dist/src/integrations/lingxiloop/calendar-approvals.d.ts +21 -0
  109. package/dist/src/integrations/lingxiloop/calendar-approvals.js +126 -0
  110. package/dist/src/integrations/lingxiloop/calendar-approvals.js.map +1 -0
  111. package/dist/src/integrations/lingxiloop/calendar-writes.d.ts +15 -0
  112. package/dist/src/integrations/lingxiloop/calendar-writes.js +79 -0
  113. package/dist/src/integrations/lingxiloop/calendar-writes.js.map +1 -0
  114. package/dist/src/integrations/lingxiloop/calendar.d.ts +19 -0
  115. package/dist/src/integrations/lingxiloop/calendar.js +43 -0
  116. package/dist/src/integrations/lingxiloop/calendar.js.map +1 -0
  117. package/dist/src/integrations/lingxiloop/canvas-events.d.ts +7 -0
  118. package/dist/src/integrations/lingxiloop/canvas-events.js +53 -0
  119. package/dist/src/integrations/lingxiloop/canvas-events.js.map +1 -0
  120. package/dist/src/integrations/lingxiloop/canvas-summary.d.ts +5 -0
  121. package/dist/src/integrations/lingxiloop/canvas-summary.js +52 -0
  122. package/dist/src/integrations/lingxiloop/canvas-summary.js.map +1 -0
  123. package/dist/src/integrations/lingxiloop/canvas-work-report.d.ts +12 -0
  124. package/dist/src/integrations/lingxiloop/canvas-work-report.js +120 -0
  125. package/dist/src/integrations/lingxiloop/canvas-work-report.js.map +1 -0
  126. package/dist/src/integrations/lingxiloop/canvas-work.d.ts +11 -0
  127. package/dist/src/integrations/lingxiloop/canvas-work.js +306 -0
  128. package/dist/src/integrations/lingxiloop/canvas-work.js.map +1 -0
  129. package/dist/src/integrations/lingxiloop/canvas.d.ts +12 -0
  130. package/dist/src/integrations/lingxiloop/canvas.js +79 -0
  131. package/dist/src/integrations/lingxiloop/canvas.js.map +1 -0
  132. package/dist/src/integrations/lingxiloop/chat.d.ts +11 -0
  133. package/dist/src/integrations/lingxiloop/chat.js +46 -0
  134. package/dist/src/integrations/lingxiloop/chat.js.map +1 -0
  135. package/dist/src/integrations/lingxiloop/document-approvals.d.ts +21 -0
  136. package/dist/src/integrations/lingxiloop/document-approvals.js +75 -0
  137. package/dist/src/integrations/lingxiloop/document-approvals.js.map +1 -0
  138. package/dist/src/integrations/lingxiloop/document-content.d.ts +80 -0
  139. package/dist/src/integrations/lingxiloop/document-content.js +195 -0
  140. package/dist/src/integrations/lingxiloop/document-content.js.map +1 -0
  141. package/dist/src/integrations/lingxiloop/document-writes.d.ts +8 -0
  142. package/dist/src/integrations/lingxiloop/document-writes.js +49 -0
  143. package/dist/src/integrations/lingxiloop/document-writes.js.map +1 -0
  144. package/dist/src/integrations/lingxiloop/documents.d.ts +22 -0
  145. package/dist/src/integrations/lingxiloop/documents.js +38 -0
  146. package/dist/src/integrations/lingxiloop/documents.js.map +1 -0
  147. package/dist/src/integrations/lingxiloop/event-outbox.d.ts +5 -0
  148. package/dist/src/integrations/lingxiloop/event-outbox.js +47 -0
  149. package/dist/src/integrations/lingxiloop/event-outbox.js.map +1 -0
  150. package/dist/src/integrations/lingxiloop/index.d.ts +3 -0
  151. package/dist/src/integrations/lingxiloop/index.js +3 -0
  152. package/dist/src/integrations/lingxiloop/index.js.map +1 -0
  153. package/dist/src/integrations/lingxiloop/learning-attempts.d.ts +13 -0
  154. package/dist/src/integrations/lingxiloop/learning-attempts.js +58 -0
  155. package/dist/src/integrations/lingxiloop/learning-attempts.js.map +1 -0
  156. package/dist/src/integrations/lingxiloop/learning-evaluation.d.ts +8 -0
  157. package/dist/src/integrations/lingxiloop/learning-evaluation.js +64 -0
  158. package/dist/src/integrations/lingxiloop/learning-evaluation.js.map +1 -0
  159. package/dist/src/integrations/lingxiloop/learning-evidence.d.ts +7 -0
  160. package/dist/src/integrations/lingxiloop/learning-evidence.js +88 -0
  161. package/dist/src/integrations/lingxiloop/learning-evidence.js.map +1 -0
  162. package/dist/src/integrations/lingxiloop/learning-missions.d.ts +18 -0
  163. package/dist/src/integrations/lingxiloop/learning-missions.js +95 -0
  164. package/dist/src/integrations/lingxiloop/learning-missions.js.map +1 -0
  165. package/dist/src/integrations/lingxiloop/learning.d.ts +22 -0
  166. package/dist/src/integrations/lingxiloop/learning.js +150 -0
  167. package/dist/src/integrations/lingxiloop/learning.js.map +1 -0
  168. package/dist/src/integrations/lingxiloop/memory.d.ts +14 -0
  169. package/dist/src/integrations/lingxiloop/memory.js +133 -0
  170. package/dist/src/integrations/lingxiloop/memory.js.map +1 -0
  171. package/dist/src/integrations/lingxiloop/polls.d.ts +9 -0
  172. package/dist/src/integrations/lingxiloop/polls.js +47 -0
  173. package/dist/src/integrations/lingxiloop/polls.js.map +1 -0
  174. package/dist/src/integrations/lingxiloop/presentations.d.ts +11 -0
  175. package/dist/src/integrations/lingxiloop/presentations.js +34 -0
  176. package/dist/src/integrations/lingxiloop/presentations.js.map +1 -0
  177. package/dist/src/integrations/lingxiloop/questionnaire.d.ts +21 -0
  178. package/dist/src/integrations/lingxiloop/questionnaire.js +57 -0
  179. package/dist/src/integrations/lingxiloop/questionnaire.js.map +1 -0
  180. package/dist/src/integrations/lingxiloop/research.d.ts +19 -0
  181. package/dist/src/integrations/lingxiloop/research.js +20 -0
  182. package/dist/src/integrations/lingxiloop/research.js.map +1 -0
  183. package/dist/src/integrations/lingxiloop/routines.d.ts +91 -0
  184. package/dist/src/integrations/lingxiloop/routines.js +198 -0
  185. package/dist/src/integrations/lingxiloop/routines.js.map +1 -0
  186. package/dist/src/integrations/lingxiloop/service-contracts.d.ts +1045 -0
  187. package/dist/src/integrations/lingxiloop/service-contracts.js +2 -0
  188. package/dist/src/integrations/lingxiloop/service-contracts.js.map +1 -0
  189. package/dist/src/integrations/lingxiloop/teacher-context.d.ts +32 -0
  190. package/dist/src/integrations/lingxiloop/teacher-context.js +25 -0
  191. package/dist/src/integrations/lingxiloop/teacher-context.js.map +1 -0
  192. package/dist/src/integrations/lingxiloop/teacher-digest.d.ts +13 -0
  193. package/dist/src/integrations/lingxiloop/teacher-digest.js +152 -0
  194. package/dist/src/integrations/lingxiloop/teacher-digest.js.map +1 -0
  195. package/dist/src/integrations/lingxiloop/teacher-preview.d.ts +22 -0
  196. package/dist/src/integrations/lingxiloop/teacher-preview.js +41 -0
  197. package/dist/src/integrations/lingxiloop/teacher-preview.js.map +1 -0
  198. package/dist/src/integrations/lingxiloop/teacher.d.ts +24 -0
  199. package/dist/src/integrations/lingxiloop/teacher.js +224 -0
  200. package/dist/src/integrations/lingxiloop/teacher.js.map +1 -0
  201. package/dist/src/kernel/manager.d.ts +52 -0
  202. package/dist/src/kernel/manager.js +407 -0
  203. package/dist/src/kernel/manager.js.map +1 -0
  204. package/dist/src/logging.d.ts +21 -0
  205. package/dist/src/logging.js +54 -0
  206. package/dist/src/logging.js.map +1 -0
  207. package/dist/src/memory/evidence.d.ts +6 -0
  208. package/dist/src/memory/evidence.js +43 -0
  209. package/dist/src/memory/evidence.js.map +1 -0
  210. package/dist/src/memory/processor.d.ts +3 -0
  211. package/dist/src/memory/processor.js +66 -0
  212. package/dist/src/memory/processor.js.map +1 -0
  213. package/dist/src/memory/semantic.d.ts +19 -0
  214. package/dist/src/memory/semantic.js +93 -0
  215. package/dist/src/memory/semantic.js.map +1 -0
  216. package/dist/src/memory/store.d.ts +20 -0
  217. package/dist/src/memory/store.js +34 -0
  218. package/dist/src/memory/store.js.map +1 -0
  219. package/dist/src/memory/synthesis.d.ts +24 -0
  220. package/dist/src/memory/synthesis.js +138 -0
  221. package/dist/src/memory/synthesis.js.map +1 -0
  222. package/dist/src/metrics.d.ts +43 -0
  223. package/dist/src/metrics.js +130 -0
  224. package/dist/src/metrics.js.map +1 -0
  225. package/dist/src/model/driver.d.ts +60 -0
  226. package/dist/src/model/driver.js +2 -0
  227. package/dist/src/model/driver.js.map +1 -0
  228. package/dist/src/model/embeddings.d.ts +24 -0
  229. package/dist/src/model/embeddings.js +97 -0
  230. package/dist/src/model/embeddings.js.map +1 -0
  231. package/dist/src/model/openai.d.ts +45 -0
  232. package/dist/src/model/openai.js +340 -0
  233. package/dist/src/model/openai.js.map +1 -0
  234. package/dist/src/outcome/assessment.d.ts +16 -0
  235. package/dist/src/outcome/assessment.js +45 -0
  236. package/dist/src/outcome/assessment.js.map +1 -0
  237. package/dist/src/outcome/content-check.d.ts +19 -0
  238. package/dist/src/outcome/content-check.js +44 -0
  239. package/dist/src/outcome/content-check.js.map +1 -0
  240. package/dist/src/outcome/envelope.d.ts +34 -0
  241. package/dist/src/outcome/envelope.js +42 -0
  242. package/dist/src/outcome/envelope.js.map +1 -0
  243. package/dist/src/protocol/constants.d.ts +25 -0
  244. package/dist/src/protocol/constants.js +26 -0
  245. package/dist/src/protocol/constants.js.map +1 -0
  246. package/dist/src/protocol/kernel-wire.d.ts +74 -0
  247. package/dist/src/protocol/kernel-wire.js +2 -0
  248. package/dist/src/protocol/kernel-wire.js.map +1 -0
  249. package/dist/src/protocol/outcome.d.ts +16 -0
  250. package/dist/src/protocol/outcome.js +14 -0
  251. package/dist/src/protocol/outcome.js.map +1 -0
  252. package/dist/src/protocol/types.d.ts +266 -0
  253. package/dist/src/protocol/types.js +19 -0
  254. package/dist/src/protocol/types.js.map +1 -0
  255. package/dist/src/research/address.d.ts +2 -0
  256. package/dist/src/research/address.js +32 -0
  257. package/dist/src/research/address.js.map +1 -0
  258. package/dist/src/research/fetch.d.ts +7 -0
  259. package/dist/src/research/fetch.js +83 -0
  260. package/dist/src/research/fetch.js.map +1 -0
  261. package/dist/src/research/index.d.ts +26 -0
  262. package/dist/src/research/index.js +109 -0
  263. package/dist/src/research/index.js.map +1 -0
  264. package/dist/src/runtime/compaction.d.ts +41 -0
  265. package/dist/src/runtime/compaction.js +86 -0
  266. package/dist/src/runtime/compaction.js.map +1 -0
  267. package/dist/src/runtime/corrections.d.ts +20 -0
  268. package/dist/src/runtime/corrections.js +27 -0
  269. package/dist/src/runtime/corrections.js.map +1 -0
  270. package/dist/src/runtime/policy.d.ts +42 -0
  271. package/dist/src/runtime/policy.js +91 -0
  272. package/dist/src/runtime/policy.js.map +1 -0
  273. package/dist/src/runtime/resource-refresh.d.ts +8 -0
  274. package/dist/src/runtime/resource-refresh.js +37 -0
  275. package/dist/src/runtime/resource-refresh.js.map +1 -0
  276. package/dist/src/runtime/runtime.d.ts +51 -0
  277. package/dist/src/runtime/runtime.js +804 -0
  278. package/dist/src/runtime/runtime.js.map +1 -0
  279. package/dist/src/runtime/tool.d.ts +13 -0
  280. package/dist/src/runtime/tool.js +42 -0
  281. package/dist/src/runtime/tool.js.map +1 -0
  282. package/dist/src/ui/index.d.ts +27 -0
  283. package/dist/src/ui/index.js +54 -0
  284. package/dist/src/ui/index.js.map +1 -0
  285. package/dist/src/versions.d.ts +9 -0
  286. package/dist/src/versions.js +11 -0
  287. package/dist/src/versions.js.map +1 -0
  288. package/dist/src/worker/index.d.ts +3 -0
  289. package/dist/src/worker/index.js +57 -0
  290. package/dist/src/worker/index.js.map +1 -0
  291. package/dist/src/worker/main.d.ts +2 -0
  292. package/dist/src/worker/main.js +9 -0
  293. package/dist/src/worker/main.js.map +1 -0
  294. package/dist/src/worker/worker.d.ts +45 -0
  295. package/dist/src/worker/worker.js +154 -0
  296. package/dist/src/worker/worker.js.map +1 -0
  297. package/kernel/runner.py +507 -0
  298. package/package.json +83 -0
@@ -0,0 +1,507 @@
1
+ """Persistent sandboxed Python worker for the LingxiOS Agent OS (protocol 2).
2
+
3
+ One runner process serves one agent session. The transport is newline-delimited
4
+ JSON over stdio; every non-ASCII character is escaped so the protocol is
5
+ locale-independent under ``python -I``.
6
+
7
+ Design invariants:
8
+
9
+ * Product operations are never implemented here. ``host.<namespace>.<method>``
10
+ emits a ``host_call`` and blocks until the matching ``host_result``. The
11
+ parent process owns authorization, approvals, idempotency, and durable state.
12
+ * The capability set is supplied per execution by the manager. Nothing is
13
+ hardcoded; an ungranted namespace or method is not even addressable.
14
+ * The in-process sandbox (audit hooks, socket patching) is defense in depth
15
+ for *accidental* misuse. Authoritative capability enforcement happens in the
16
+ control plane, which validates every action against the durable grant.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import ast
22
+ import base64
23
+ import contextlib
24
+ import hashlib
25
+ import io
26
+ import json
27
+ import mimetypes
28
+ import os
29
+ import pathlib
30
+ import socket
31
+ import sys
32
+ import time
33
+ import traceback
34
+ import types
35
+ import uuid
36
+ from typing import Any
37
+
38
+ KERNEL_PROTOCOL_VERSION = 2
39
+ SDK_MODULE_NAME = "host"
40
+
41
+ MAX_STREAM_CHARS = int(os.environ.get("AGENT_OS_KERNEL_MAX_OUTPUT_CHARS", "8000"))
42
+ MAX_ARTIFACTS = int(os.environ.get("AGENT_OS_KERNEL_MAX_ARTIFACTS", "32"))
43
+ ROOT = pathlib.Path(os.environ["AGENT_OS_KERNEL_HOME"]).resolve()
44
+ HOMES_ROOT = pathlib.Path(os.environ.get("AGENT_OS_HOMES_ROOT", str(ROOT.parent))).resolve()
45
+ ALLOW_NETWORK = os.environ.get("AGENT_OS_KERNEL_ALLOW_NETWORK") == "1"
46
+ ROOT.mkdir(parents=True, exist_ok=True)
47
+ os.chdir(ROOT)
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Wire helpers
52
+ # ---------------------------------------------------------------------------
53
+
54
+ def emit(payload: dict[str, Any]) -> None:
55
+ sys.__stdout__.write(json.dumps(payload, ensure_ascii=True, default=str) + "\n")
56
+ sys.__stdout__.flush()
57
+
58
+
59
+ def read_message() -> dict[str, Any]:
60
+ line = sys.__stdin__.readline()
61
+ if not line:
62
+ raise EOFError()
63
+ value = json.loads(line)
64
+ if not isinstance(value, dict):
65
+ raise ValueError("protocol message must be a JSON object")
66
+ return value
67
+
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # Sandbox: network, filesystem, and child-process fences
71
+ # ---------------------------------------------------------------------------
72
+
73
+ def deny_network(*_args: Any, **_kwargs: Any) -> Any:
74
+ raise PermissionError(
75
+ "direct network access is disabled; use a granted host.<namespace> capability"
76
+ )
77
+
78
+
79
+ if not ALLOW_NETWORK:
80
+ socket.create_connection = deny_network # type: ignore[assignment]
81
+ socket.socket.connect = deny_network # type: ignore[assignment]
82
+
83
+
84
+ def is_within(path: pathlib.Path, parent: pathlib.Path) -> bool:
85
+ try:
86
+ path.relative_to(parent)
87
+ return True
88
+ except ValueError:
89
+ return False
90
+
91
+
92
+ def kernel_audit(event: str, args: tuple[Any, ...]) -> None:
93
+ if event == "open" and args and isinstance(args[0], (str, bytes, os.PathLike)):
94
+ path = pathlib.Path(os.fsdecode(args[0])).resolve()
95
+ mode = str(args[1]) if len(args) > 1 else "r"
96
+ if is_within(path, HOMES_ROOT) and not is_within(path, ROOT):
97
+ raise PermissionError("cross-session file access is disabled")
98
+ flags = args[2] if len(args) > 2 and isinstance(args[2], int) else 0
99
+ writing = any(flag in mode for flag in ("w", "a", "x", "+")) or flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND)
100
+ if writing and not is_within(path, ROOT):
101
+ raise PermissionError("files may only be written inside this agent home")
102
+ if event in {"os.link", "os.symlink"}:
103
+ raise PermissionError("creating filesystem links is disabled in this kernel")
104
+ mutations = {
105
+ "os.remove": (1, (1,)), "os.rmdir": (1, (1,)), "os.mkdir": (1, (2,)),
106
+ "os.rename": (2, (2, 3)), "os.chmod": (1, (2,)), "os.chown": (1, (3,)),
107
+ "os.utime": (1, (3,)), "os.truncate": (1, ()),
108
+ }
109
+ if event in mutations:
110
+ count, descriptor_indices = mutations[event]
111
+ if any(len(args) > index and args[index] not in (-1, None) for index in descriptor_indices):
112
+ raise PermissionError("descriptor-relative filesystem mutations are disabled")
113
+ for target in args[:count]:
114
+ if not isinstance(target, (str, bytes, os.PathLike)) or not is_within(pathlib.Path(os.fsdecode(target)).resolve(), ROOT):
115
+ raise PermissionError("filesystem mutations must stay inside this agent home")
116
+ if event in {"os.system", "subprocess.Popen"}:
117
+ # A child process would escape the in-process audit fences, so shell
118
+ # effects must go through a typed host capability instead.
119
+ raise PermissionError("direct child processes are disabled in this kernel sandbox")
120
+ if event.startswith("socket.") and not ALLOW_NETWORK:
121
+ raise PermissionError("direct network access is disabled")
122
+
123
+
124
+ sys.addaudithook(kernel_audit)
125
+
126
+
127
+ # ---------------------------------------------------------------------------
128
+ # Host bridge
129
+ # ---------------------------------------------------------------------------
130
+
131
+ class TurnDeferred(BaseException):
132
+ """Stop this cell after a host requests a human or delegated wait."""
133
+
134
+
135
+ class ApprovalPending(RuntimeError):
136
+ """Raised when a host action suspended into a human approval."""
137
+
138
+ def __init__(self, approval_id: str):
139
+ super().__init__(f"approval pending: {approval_id}")
140
+ self.approval_id = approval_id
141
+
142
+
143
+ class HostBridge:
144
+ def __init__(self) -> None:
145
+ self.execution_id = ""
146
+ self.run_id = ""
147
+ self.cell_id = ""
148
+ self.call_index = 0
149
+ self.directives: list[dict[str, Any]] = []
150
+ self.capabilities: dict[str, frozenset[str] | None] = {}
151
+
152
+ def begin(self, execution_id: str, context: dict[str, Any]) -> None:
153
+ self.execution_id = execution_id
154
+ self.run_id = str(context.get("runId", ""))
155
+ self.cell_id = str(context.get("cellId", execution_id))
156
+ self.call_index = 0
157
+ self.directives = []
158
+ self.capabilities = {}
159
+ for grant in context.get("capabilities") or []:
160
+ if not isinstance(grant, dict):
161
+ continue
162
+ name = grant.get("name")
163
+ if not isinstance(name, str) or not name.isidentifier() or name.startswith("_"):
164
+ continue
165
+ methods = grant.get("methods")
166
+ allowed: frozenset[str] | None = None
167
+ if isinstance(methods, list):
168
+ allowed = frozenset(m for m in methods if isinstance(m, str) and m.isidentifier())
169
+ self.capabilities[name] = allowed
170
+
171
+ def allows(self, namespace: str, method: str) -> bool:
172
+ if namespace not in self.capabilities or method.startswith("_"):
173
+ return False
174
+ allowed = self.capabilities[namespace]
175
+ return allowed is None or method in allowed
176
+
177
+ def call(self, action: str, args: dict[str, Any]) -> Any:
178
+ if any(directive.get("type") == "defer" for directive in self.directives):
179
+ raise TurnDeferred()
180
+ index = self.call_index
181
+ self.call_index += 1
182
+ request_id = str(uuid.uuid4())
183
+ emit({
184
+ "type": "host_call",
185
+ "id": self.execution_id,
186
+ "requestId": request_id,
187
+ "callIndex": index,
188
+ "action": action,
189
+ "args": args,
190
+ })
191
+ response = read_message()
192
+ if response.get("type") != "host_result" or response.get("requestId") != request_id:
193
+ raise RuntimeError("unexpected host bridge response")
194
+ approval = response.get("approval")
195
+ if isinstance(approval, dict) and approval.get("id"):
196
+ raise ApprovalPending(str(approval["id"]))
197
+ if not response.get("ok"):
198
+ raise RuntimeError(str(response.get("error") or "host action failed"))
199
+ if isinstance(response.get("directive"), dict):
200
+ self.directives.append(response["directive"])
201
+ if response["directive"].get("type") == "defer":
202
+ raise TurnDeferred()
203
+ return response.get("value")
204
+
205
+
206
+ bridge = HostBridge()
207
+
208
+
209
+ class _NamespaceModule(types.ModuleType):
210
+ """`host.<namespace>` — methods resolve lazily against the current grant."""
211
+
212
+ def __init__(self, namespace: str):
213
+ super().__init__(f"{SDK_MODULE_NAME}.{namespace}")
214
+ self._namespace = namespace
215
+
216
+ def __getattr__(self, method: str) -> Any:
217
+ if not bridge.allows(self._namespace, method):
218
+ raise AttributeError(
219
+ f"capability '{self._namespace}.{method}' is not granted to this execution"
220
+ )
221
+ namespace = self._namespace
222
+
223
+ def invoke(**kwargs: Any) -> Any:
224
+ return bridge.call(f"{namespace}.{method}", kwargs)
225
+
226
+ invoke.__name__ = method
227
+ return invoke
228
+
229
+
230
+ class _SdkModule(types.ModuleType):
231
+ """`host` — namespaces resolve lazily against the current grant."""
232
+
233
+ def __getattr__(self, namespace: str) -> Any:
234
+ if namespace.startswith("_") or namespace not in bridge.capabilities:
235
+ raise AttributeError(
236
+ f"capability namespace '{namespace}' is not granted to this execution"
237
+ )
238
+ module = _NamespaceModule(namespace)
239
+ sys.modules[module.__name__] = module
240
+ return module
241
+
242
+
243
+ sdk = _SdkModule(SDK_MODULE_NAME)
244
+ sdk.__path__ = [] # mark as a package so `import host.files` resolves
245
+ sys.modules[SDK_MODULE_NAME] = sdk
246
+
247
+
248
+ # ---------------------------------------------------------------------------
249
+ # Cell execution engines
250
+ # ---------------------------------------------------------------------------
251
+
252
+ class CellOutcome:
253
+ def __init__(self, result: Any = None, error: BaseException | None = None):
254
+ self.result = result
255
+ self.error = error
256
+
257
+
258
+ class BasicEngine:
259
+ """Fallback engine when IPython is unavailable: exec with expression-result
260
+ semantics (the value of a trailing expression becomes the cell result)."""
261
+
262
+ name = "basic"
263
+
264
+ def __init__(self) -> None:
265
+ self.user_ns: dict[str, Any] = {"__name__": "__main__"}
266
+
267
+ def run_cell(self, code: str) -> CellOutcome:
268
+ try:
269
+ tree = ast.parse(code, mode="exec")
270
+ except SyntaxError as exc:
271
+ return CellOutcome(error=exc)
272
+ trailing_expr: ast.Expression | None = None
273
+ if tree.body and isinstance(tree.body[-1], ast.Expr):
274
+ trailing_expr = ast.Expression(tree.body.pop(-1).value)
275
+ try:
276
+ if tree.body:
277
+ exec(compile(tree, "<cell>", "exec"), self.user_ns) # noqa: S102
278
+ if trailing_expr is not None:
279
+ return CellOutcome(result=eval(compile(trailing_expr, "<cell>", "eval"), self.user_ns)) # noqa: S307
280
+ return CellOutcome()
281
+ except BaseException as exc: # cells may raise non-Exception exits
282
+ return CellOutcome(error=exc)
283
+
284
+
285
+ class IPythonEngine:
286
+ name = "ipython"
287
+
288
+ def __init__(self, shell: Any):
289
+ self.shell = shell
290
+ self.user_ns = shell.user_ns
291
+
292
+ def run_cell(self, code: str) -> CellOutcome:
293
+ result = self.shell.run_cell(code, store_history=True)
294
+ if result.error_before_exec is not None:
295
+ return CellOutcome(error=result.error_before_exec)
296
+ if result.error_in_exec is not None:
297
+ return CellOutcome(error=result.error_in_exec)
298
+ return CellOutcome(result=result.result)
299
+
300
+
301
+ def create_engine() -> BasicEngine | IPythonEngine:
302
+ try:
303
+ from IPython.core.interactiveshell import InteractiveShell # noqa: PLC0415
304
+ except ImportError:
305
+ return BasicEngine()
306
+ return IPythonEngine(InteractiveShell.instance())
307
+
308
+
309
+ engine = create_engine()
310
+ engine.user_ns[SDK_MODULE_NAME] = sdk
311
+ engine.user_ns["ApprovalPending"] = ApprovalPending
312
+
313
+
314
+ # ---------------------------------------------------------------------------
315
+ # Result shaping and artifact tracking
316
+ # ---------------------------------------------------------------------------
317
+
318
+ def safe_result(value: Any) -> Any:
319
+ if value is None or isinstance(value, (str, int, float, bool, list, dict)):
320
+ encoded = json.dumps(value, ensure_ascii=False, default=str)
321
+ if len(encoded) <= MAX_STREAM_CHARS:
322
+ return value
323
+ return {"truncated": True, "preview": encoded[: MAX_STREAM_CHARS - 80]}
324
+ if hasattr(value, "_repr_mimebundle_"):
325
+ bundle = value._repr_mimebundle_()
326
+ if isinstance(bundle, tuple):
327
+ bundle = bundle[0]
328
+ if isinstance(bundle, dict):
329
+ encoded = json.dumps(bundle, ensure_ascii=False, default=str)
330
+ if len(encoded) <= MAX_STREAM_CHARS:
331
+ return {"mimeBundle": bundle}
332
+ rich: dict[str, str] = {}
333
+ for method, mime in (
334
+ ("_repr_png_", "image/png"),
335
+ ("_repr_svg_", "image/svg+xml"),
336
+ ("_repr_html_", "text/html"),
337
+ ):
338
+ renderer = getattr(value, method, None)
339
+ if not callable(renderer):
340
+ continue
341
+ try:
342
+ rendered = renderer()
343
+ except Exception:
344
+ continue
345
+ if rendered is None:
346
+ continue
347
+ if isinstance(rendered, bytes):
348
+ rendered = base64.b64encode(rendered).decode("ascii")
349
+ rich[mime] = str(rendered)[:MAX_STREAM_CHARS]
350
+ if rich:
351
+ return {"mimeBundle": rich}
352
+ return repr(value)[:MAX_STREAM_CHARS]
353
+
354
+
355
+ def file_snapshot() -> dict[str, tuple[int, int]]:
356
+ found: dict[str, tuple[int, int]] = {}
357
+ for path in ROOT.rglob("*"):
358
+ try:
359
+ if is_within(path.resolve(), ROOT) and path.is_file() and not path.is_symlink():
360
+ stat = path.stat()
361
+ found[path.relative_to(ROOT).as_posix()] = (stat.st_size, stat.st_mtime_ns)
362
+ except OSError:
363
+ continue
364
+ return found
365
+
366
+
367
+ attached_files: set[str] = set()
368
+
369
+
370
+ def attach_file(path: str) -> None:
371
+ """Include an existing home file in this cell's checked artifact output."""
372
+ if not isinstance(path, str) or not path or len(path) > 4096:
373
+ raise ValueError('attach_file requires a file path')
374
+ target = (ROOT / path).resolve()
375
+ if not is_within(target, ROOT) or not target.is_file():
376
+ raise ValueError('attached file must exist inside this agent home')
377
+ relative = target.relative_to(ROOT).as_posix()
378
+ if relative not in attached_files and len(attached_files) >= MAX_ARTIFACTS:
379
+ raise ValueError('too many attached files')
380
+ attached_files.add(relative)
381
+
382
+
383
+ engine.user_ns['attach_file'] = attach_file
384
+
385
+
386
+ def changed_artifacts(before: dict[str, tuple[int, int]]) -> list[dict[str, Any]]:
387
+ artifacts: list[dict[str, Any]] = []
388
+ current = file_snapshot()
389
+ if attached_files - current.keys():
390
+ raise ValueError('an attached file is no longer available inside this agent home')
391
+ for relative in sorted(current, key=lambda path: path not in attached_files):
392
+ state = current[relative]
393
+ if before.get(relative) == state and relative not in attached_files:
394
+ continue
395
+ path = ROOT / relative
396
+ try:
397
+ if not is_within(path.resolve(), ROOT) or path.is_symlink():
398
+ continue
399
+ digest = hashlib.sha256()
400
+ bytes_read = 0
401
+ with path.open('rb') as stream:
402
+ for chunk in iter(lambda: stream.read(1024 * 1024), b''):
403
+ digest.update(chunk)
404
+ bytes_read += len(chunk)
405
+ final_stat = os.fstat(stream.fileno())
406
+ if bytes_read != state[0] or (final_stat.st_size, final_stat.st_mtime_ns) != state:
407
+ raise OSError('artifact changed while being hashed')
408
+ except OSError:
409
+ if relative in attached_files:
410
+ raise
411
+ continue
412
+ artifacts.append({
413
+ "path": relative,
414
+ "size": state[0],
415
+ "mime": mimetypes.guess_type(relative)[0] or "application/octet-stream",
416
+ "sha256": digest.hexdigest(),
417
+ })
418
+ if len(artifacts) >= MAX_ARTIFACTS:
419
+ break
420
+ return artifacts
421
+
422
+
423
+ # ---------------------------------------------------------------------------
424
+ # Main loop
425
+ # ---------------------------------------------------------------------------
426
+
427
+ def execute(message: dict[str, Any]) -> None:
428
+ execution_id = str(message["id"])
429
+ code = str(message["code"])
430
+ bridge.begin(execution_id, message.get("context") or {})
431
+ attached_files.clear()
432
+ stdout = io.StringIO()
433
+ stderr = io.StringIO()
434
+ started = time.monotonic()
435
+ files_before = file_snapshot()
436
+ result_value: Any = None
437
+ error: str | None = None
438
+ approval_id: str | None = None
439
+ try:
440
+ with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
441
+ outcome = engine.run_cell(code)
442
+ if outcome.error is not None:
443
+ raise outcome.error
444
+ result_value = safe_result(outcome.result)
445
+ except ApprovalPending as pending:
446
+ approval_id = pending.approval_id
447
+ except TurnDeferred:
448
+ pass
449
+ except BaseException as exc:
450
+ error = "".join(traceback.format_exception_only(type(exc), exc)).strip()
451
+
452
+ out = stdout.getvalue()
453
+ err = stderr.getvalue()
454
+ truncated = len(out) + len(err) > MAX_STREAM_CHARS
455
+ if truncated:
456
+ remaining = MAX_STREAM_CHARS
457
+ out = out[:remaining]
458
+ remaining -= len(out)
459
+ err = err[: max(0, remaining)]
460
+ try:
461
+ artifacts = changed_artifacts(files_before)
462
+ except (OSError, ValueError) as exc:
463
+ artifacts = []
464
+ error = str(exc)
465
+ emit({
466
+ "type": "execution_result",
467
+ "id": execution_id,
468
+ "ok": error is None and approval_id is None,
469
+ "stdout": out,
470
+ "stderr": err,
471
+ "result": result_value,
472
+ "error": error,
473
+ "approvalId": approval_id,
474
+ "truncated": truncated,
475
+ "durationMs": round((time.monotonic() - started) * 1000),
476
+ "artifacts": artifacts,
477
+ "directives": bridge.directives,
478
+ })
479
+
480
+
481
+ def main() -> None:
482
+ emit({
483
+ "type": "ready",
484
+ "protocol": KERNEL_PROTOCOL_VERSION,
485
+ "python": sys.version.split()[0],
486
+ "engine": engine.name,
487
+ "home": str(ROOT),
488
+ })
489
+ while True:
490
+ try:
491
+ message = read_message()
492
+ except EOFError:
493
+ return
494
+ except Exception as exc:
495
+ emit({"type": "protocol_error", "error": str(exc)})
496
+ continue
497
+ message_type = message.get("type")
498
+ if message_type == "shutdown":
499
+ return
500
+ if message_type != "execute":
501
+ emit({"type": "protocol_error", "error": "expected execute"})
502
+ continue
503
+ execute(message)
504
+
505
+
506
+ if __name__ == "__main__":
507
+ main()
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "lingxios",
3
+ "version": "1.0.0",
4
+ "description": "LingxiOS — a purpose-built, production-grade Agent OS: durable work orchestration, sandboxed code-act kernels, and a policy-extensible agent runtime.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/lyyzka/LingxiOS.git"
10
+ },
11
+ "files": [
12
+ "dist/src",
13
+ "db/schema.sql",
14
+ "kernel/runner.py"
15
+ ],
16
+ "engines": {
17
+ "node": ">=22.13.0"
18
+ },
19
+ "main": "dist/src/index.js",
20
+ "types": "dist/src/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/src/index.d.ts",
24
+ "import": "./dist/src/index.js"
25
+ },
26
+ "./worker": {
27
+ "types": "./dist/src/worker/index.d.ts",
28
+ "import": "./dist/src/worker/index.js"
29
+ },
30
+ "./eval": {
31
+ "types": "./dist/src/eval/index.d.ts",
32
+ "import": "./dist/src/eval/index.js"
33
+ },
34
+ "./ui": {
35
+ "types": "./dist/src/ui/index.d.ts",
36
+ "import": "./dist/src/ui/index.js"
37
+ },
38
+ "./lingxiloop": {
39
+ "types": "./dist/src/integrations/lingxiloop/index.d.ts",
40
+ "import": "./dist/src/integrations/lingxiloop/index.js"
41
+ }
42
+ },
43
+ "bin": {
44
+ "lingxios-worker": "dist/src/worker/main.js",
45
+ "lingxios-eval": "dist/src/cli/eval.js",
46
+ "lingxios-doctor": "dist/src/cli/doctor.js"
47
+ },
48
+ "scripts": {
49
+ "build": "node scripts/build.mjs",
50
+ "prepack": "npm run build",
51
+ "prepublishOnly": "npm run check:release",
52
+ "check:release": "npm test && npm run test:contracts && npm run test:postgres-stores && npm run test:worker-recovery && npm run test:native-calendar && npm run test:native-documents && npm run test:native-document-content && npm run test:native-polls && npm run test:native-presentations && npm run test:native-teacher && npm run test:native-missions && npm run test:native-evidence && npm run test:native-canvas && npm run test:native-canvas-work && npm run test:native-learning",
53
+ "typecheck": "tsc -p tsconfig.json --noEmit",
54
+ "test": "npm run build && node --test --test-concurrency=1 \"dist/test/**/*.test.js\"",
55
+ "test:only": "node --test --test-concurrency=1 \"dist/test/**/*.test.js\"",
56
+ "eval:live": "npm run build && node scripts/eval-live.mjs",
57
+ "test:contracts": "node scripts/check-lingxiloop-contracts.mjs",
58
+ "test:postgres-stores": "npm run build && node scripts/test-postgres-stores.mjs",
59
+ "test:native-missions": "npm run build && node scripts/test-native-missions.mjs",
60
+ "test:native-learning": "node scripts/test-native-learning.mjs",
61
+ "test:native-evidence": "npm run build && node scripts/test-native-evidence.mjs",
62
+ "test:native-polls": "npm run build && node scripts/test-native-polls.mjs",
63
+ "test:native-presentations": "npm run build && node scripts/test-native-presentations.mjs",
64
+ "worker": "node dist/src/worker/main.js",
65
+ "test:worker-image": "node scripts/test-worker-image.mjs",
66
+ "test:worker-recovery": "npm run build && node scripts/test-worker-recovery.mjs",
67
+ "test:native-teacher": "node scripts/test-native-teacher.mjs",
68
+ "test:native-canvas": "node scripts/test-native-canvas.mjs",
69
+ "test:native-canvas-work": "npm run build && node scripts/test-native-canvas-work.mjs",
70
+ "test:native-documents": "npm run build && node scripts/test-native-documents.mjs",
71
+ "test:native-document-content": "npm run build && node scripts/test-native-document-content.mjs",
72
+ "test:native-calendar": "npm run build && node scripts/test-native-calendar.mjs"
73
+ },
74
+ "devDependencies": {
75
+ "@electric-sql/pglite": "0.5.8",
76
+ "@types/node": "^20.17.0",
77
+ "typescript": "^5.7.0"
78
+ },
79
+ "dependencies": {
80
+ "mammoth": "1.12.2",
81
+ "pdfjs-dist": "6.3.289"
82
+ }
83
+ }