@animalabs/agent-framework 0.7.4 → 0.9.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 (237) hide show
  1. package/dist/src/agent.d.ts +10 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +33 -3
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/code-execution/index.d.ts +1 -1
  6. package/dist/src/code-execution/index.d.ts.map +1 -1
  7. package/dist/src/code-execution/index.js.map +1 -1
  8. package/dist/src/code-execution/py-runner.d.ts +23 -1
  9. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  10. package/dist/src/code-execution/py-runner.js +40 -4
  11. package/dist/src/code-execution/py-runner.js.map +1 -1
  12. package/dist/src/code-execution/runtime-py.d.ts +15 -2
  13. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  14. package/dist/src/code-execution/runtime-py.js +156 -7
  15. package/dist/src/code-execution/runtime-py.js.map +1 -1
  16. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  17. package/dist/src/code-execution/tool-definition.js +27 -2
  18. package/dist/src/code-execution/tool-definition.js.map +1 -1
  19. package/dist/src/framework.d.ts +234 -5
  20. package/dist/src/framework.d.ts.map +1 -1
  21. package/dist/src/framework.js +1818 -188
  22. package/dist/src/framework.js.map +1 -1
  23. package/dist/src/gate/event-gate.d.ts +11 -0
  24. package/dist/src/gate/event-gate.d.ts.map +1 -1
  25. package/dist/src/gate/event-gate.js +66 -0
  26. package/dist/src/gate/event-gate.js.map +1 -1
  27. package/dist/src/index.d.ts +1 -0
  28. package/dist/src/index.d.ts.map +1 -1
  29. package/dist/src/index.js +1 -0
  30. package/dist/src/index.js.map +1 -1
  31. package/dist/src/mcpl/capability-grant.d.ts +115 -0
  32. package/dist/src/mcpl/capability-grant.d.ts.map +1 -0
  33. package/dist/src/mcpl/capability-grant.js +291 -0
  34. package/dist/src/mcpl/capability-grant.js.map +1 -0
  35. package/dist/src/mcpl/capability-mask.d.ts +51 -0
  36. package/dist/src/mcpl/capability-mask.d.ts.map +1 -0
  37. package/dist/src/mcpl/capability-mask.js +190 -0
  38. package/dist/src/mcpl/capability-mask.js.map +1 -0
  39. package/dist/src/mcpl/channel-registry.d.ts +30 -2
  40. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  41. package/dist/src/mcpl/channel-registry.js +216 -43
  42. package/dist/src/mcpl/channel-registry.js.map +1 -1
  43. package/dist/src/mcpl/checkpoint-manager.d.ts +8 -3
  44. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  45. package/dist/src/mcpl/checkpoint-manager.js +28 -8
  46. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  47. package/dist/src/mcpl/errors.d.ts +6 -0
  48. package/dist/src/mcpl/errors.d.ts.map +1 -1
  49. package/dist/src/mcpl/errors.js +6 -0
  50. package/dist/src/mcpl/errors.js.map +1 -1
  51. package/dist/src/mcpl/feature-set-manager.d.ts +10 -1
  52. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  53. package/dist/src/mcpl/feature-set-manager.js +45 -4
  54. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  55. package/dist/src/mcpl/hook-orchestrator.d.ts +10 -14
  56. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  57. package/dist/src/mcpl/hook-orchestrator.js +58 -67
  58. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  59. package/dist/src/mcpl/index.d.ts +1 -1
  60. package/dist/src/mcpl/index.d.ts.map +1 -1
  61. package/dist/src/mcpl/index.js.map +1 -1
  62. package/dist/src/mcpl/prose-stream-router.d.ts +9 -0
  63. package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
  64. package/dist/src/mcpl/prose-stream-router.js +16 -0
  65. package/dist/src/mcpl/prose-stream-router.js.map +1 -1
  66. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  67. package/dist/src/mcpl/push-handler.js +20 -2
  68. package/dist/src/mcpl/push-handler.js.map +1 -1
  69. package/dist/src/mcpl/server-connection.d.ts +93 -9
  70. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  71. package/dist/src/mcpl/server-connection.js +231 -32
  72. package/dist/src/mcpl/server-connection.js.map +1 -1
  73. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  74. package/dist/src/mcpl/server-registry.js +4 -3
  75. package/dist/src/mcpl/server-registry.js.map +1 -1
  76. package/dist/src/mcpl/tags.d.ts +24 -0
  77. package/dist/src/mcpl/tags.d.ts.map +1 -0
  78. package/dist/src/mcpl/tags.js +52 -0
  79. package/dist/src/mcpl/tags.js.map +1 -0
  80. package/dist/src/mcpl/transport.d.ts +1 -1
  81. package/dist/src/mcpl/transport.d.ts.map +1 -1
  82. package/dist/src/mcpl/transport.js +35 -7
  83. package/dist/src/mcpl/transport.js.map +1 -1
  84. package/dist/src/mcpl/types.d.ts +169 -64
  85. package/dist/src/mcpl/types.d.ts.map +1 -1
  86. package/dist/src/mcpl/types.js +16 -2
  87. package/dist/src/mcpl/types.js.map +1 -1
  88. package/dist/src/module-registry.d.ts +12 -0
  89. package/dist/src/module-registry.d.ts.map +1 -1
  90. package/dist/src/module-registry.js +26 -0
  91. package/dist/src/module-registry.js.map +1 -1
  92. package/dist/src/modules/workspace/index.d.ts +20 -0
  93. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  94. package/dist/src/modules/workspace/index.js +48 -15
  95. package/dist/src/modules/workspace/index.js.map +1 -1
  96. package/dist/src/modules/workspace/sync.d.ts +4 -0
  97. package/dist/src/modules/workspace/sync.d.ts.map +1 -1
  98. package/dist/src/modules/workspace/sync.js +1 -1
  99. package/dist/src/modules/workspace/sync.js.map +1 -1
  100. package/dist/src/refusal-reactions.d.ts +27 -0
  101. package/dist/src/refusal-reactions.d.ts.map +1 -0
  102. package/dist/src/refusal-reactions.js +35 -0
  103. package/dist/src/refusal-reactions.js.map +1 -0
  104. package/dist/src/tool-result-history.d.ts +10 -0
  105. package/dist/src/tool-result-history.d.ts.map +1 -1
  106. package/dist/src/tool-result-history.js +16 -5
  107. package/dist/src/tool-result-history.js.map +1 -1
  108. package/dist/src/types/agent.d.ts +35 -0
  109. package/dist/src/types/agent.d.ts.map +1 -1
  110. package/dist/src/types/framework.d.ts +26 -0
  111. package/dist/src/types/framework.d.ts.map +1 -1
  112. package/dist/src/types/module.d.ts +22 -0
  113. package/dist/src/types/module.d.ts.map +1 -1
  114. package/dist/src/types/trace.d.ts +24 -0
  115. package/dist/src/types/trace.d.ts.map +1 -1
  116. package/dist/test/busy-agent-scheduler.test.js +1 -0
  117. package/dist/test/busy-agent-scheduler.test.js.map +1 -1
  118. package/dist/test/channel-registry-routing.test.js +38 -10
  119. package/dist/test/channel-registry-routing.test.js.map +1 -1
  120. package/dist/test/channel-registry-typing.test.js +10 -1
  121. package/dist/test/channel-registry-typing.test.js.map +1 -1
  122. package/dist/test/checkpoint-attribution.test.js +168 -8
  123. package/dist/test/checkpoint-attribution.test.js.map +1 -1
  124. package/dist/test/code-execution.test.js +327 -1
  125. package/dist/test/code-execution.test.js.map +1 -1
  126. package/dist/test/conversation-fork-injection-scoping.test.js +8 -3
  127. package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -1
  128. package/dist/test/deferred-wake-integrity.test.d.ts +29 -0
  129. package/dist/test/deferred-wake-integrity.test.d.ts.map +1 -0
  130. package/dist/test/deferred-wake-integrity.test.js +320 -0
  131. package/dist/test/deferred-wake-integrity.test.js.map +1 -0
  132. package/dist/test/event-gate-selfwake.test.d.ts +15 -0
  133. package/dist/test/event-gate-selfwake.test.d.ts.map +1 -0
  134. package/dist/test/event-gate-selfwake.test.js +125 -0
  135. package/dist/test/event-gate-selfwake.test.js.map +1 -0
  136. package/dist/test/machine-close-provenance.test.d.ts +2 -0
  137. package/dist/test/machine-close-provenance.test.d.ts.map +1 -0
  138. package/dist/test/machine-close-provenance.test.js +116 -0
  139. package/dist/test/machine-close-provenance.test.js.map +1 -0
  140. package/dist/test/mcpl-access-provider.test.d.ts +2 -0
  141. package/dist/test/mcpl-access-provider.test.d.ts.map +1 -0
  142. package/dist/test/mcpl-access-provider.test.js +20 -0
  143. package/dist/test/mcpl-access-provider.test.js.map +1 -0
  144. package/dist/test/mcpl-awareness-barrier.test.js +10 -0
  145. package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
  146. package/dist/test/mcpl-capability-grant.test.d.ts +10 -0
  147. package/dist/test/mcpl-capability-grant.test.d.ts.map +1 -0
  148. package/dist/test/mcpl-capability-grant.test.js +109 -0
  149. package/dist/test/mcpl-capability-grant.test.js.map +1 -0
  150. package/dist/test/mcpl-capability-scoping.test.d.ts +2 -0
  151. package/dist/test/mcpl-capability-scoping.test.d.ts.map +1 -0
  152. package/dist/test/mcpl-capability-scoping.test.js +266 -0
  153. package/dist/test/mcpl-capability-scoping.test.js.map +1 -0
  154. package/dist/test/mcpl-gate-roundtrip.test.js +13 -0
  155. package/dist/test/mcpl-gate-roundtrip.test.js.map +1 -1
  156. package/dist/test/mcpl-list-status.test.d.ts +2 -0
  157. package/dist/test/mcpl-list-status.test.d.ts.map +1 -0
  158. package/dist/test/mcpl-list-status.test.js +78 -0
  159. package/dist/test/mcpl-list-status.test.js.map +1 -0
  160. package/dist/test/mcpl-manifest-changed.test.d.ts +2 -0
  161. package/dist/test/mcpl-manifest-changed.test.d.ts.map +1 -0
  162. package/dist/test/mcpl-manifest-changed.test.js +177 -0
  163. package/dist/test/mcpl-manifest-changed.test.js.map +1 -0
  164. package/dist/test/mcpl-reconnect-events.test.js +27 -2
  165. package/dist/test/mcpl-reconnect-events.test.js.map +1 -1
  166. package/dist/test/mcpl-reconnect-reregistration.test.js +53 -17
  167. package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -1
  168. package/dist/test/mcpl-request-timeout.test.js +1 -1
  169. package/dist/test/mcpl-request-timeout.test.js.map +1 -1
  170. package/dist/test/module-context-integration.test.d.ts +18 -0
  171. package/dist/test/module-context-integration.test.d.ts.map +1 -0
  172. package/dist/test/module-context-integration.test.js +181 -0
  173. package/dist/test/module-context-integration.test.js.map +1 -0
  174. package/dist/test/physical-window-restart.test.d.ts +14 -0
  175. package/dist/test/physical-window-restart.test.d.ts.map +1 -0
  176. package/dist/test/physical-window-restart.test.js +222 -0
  177. package/dist/test/physical-window-restart.test.js.map +1 -0
  178. package/dist/test/present-while-acting.test.js +270 -0
  179. package/dist/test/present-while-acting.test.js.map +1 -1
  180. package/dist/test/refusal-reaction-baseline.test.d.ts +2 -0
  181. package/dist/test/refusal-reaction-baseline.test.d.ts.map +1 -0
  182. package/dist/test/refusal-reaction-baseline.test.js +74 -0
  183. package/dist/test/refusal-reaction-baseline.test.js.map +1 -0
  184. package/dist/test/state-scaling-e2e.test.js +7 -1
  185. package/dist/test/state-scaling-e2e.test.js.map +1 -1
  186. package/dist/test/state-scaling.test.js +9 -1
  187. package/dist/test/state-scaling.test.js.map +1 -1
  188. package/dist/test/tool-result-spill.test.d.ts +13 -0
  189. package/dist/test/tool-result-spill.test.d.ts.map +1 -0
  190. package/dist/test/tool-result-spill.test.js +463 -0
  191. package/dist/test/tool-result-spill.test.js.map +1 -0
  192. package/dist/test/trunk-channel-routing.test.js +45 -0
  193. package/dist/test/trunk-channel-routing.test.js.map +1 -1
  194. package/dist/test/utils-surface.test.d.ts +2 -0
  195. package/dist/test/utils-surface.test.d.ts.map +1 -0
  196. package/dist/test/utils-surface.test.js +145 -0
  197. package/dist/test/utils-surface.test.js.map +1 -0
  198. package/dist/test/workspace-read-image.test.js +71 -0
  199. package/dist/test/workspace-read-image.test.js.map +1 -1
  200. package/dist/test/workspace-restore-order.test.d.ts +16 -0
  201. package/dist/test/workspace-restore-order.test.d.ts.map +1 -0
  202. package/dist/test/workspace-restore-order.test.js +140 -0
  203. package/dist/test/workspace-restore-order.test.js.map +1 -0
  204. package/dist/tsconfig.tsbuildinfo +1 -1
  205. package/package.json +4 -4
  206. package/src/agent.ts +37 -3
  207. package/src/code-execution/index.ts +1 -0
  208. package/src/code-execution/py-runner.ts +56 -4
  209. package/src/code-execution/runtime-py.ts +156 -7
  210. package/src/code-execution/tool-definition.ts +27 -2
  211. package/src/framework.ts +2080 -203
  212. package/src/gate/event-gate.ts +72 -0
  213. package/src/index.ts +1 -0
  214. package/src/mcpl/capability-grant.ts +319 -0
  215. package/src/mcpl/capability-mask.ts +228 -0
  216. package/src/mcpl/channel-registry.ts +265 -43
  217. package/src/mcpl/checkpoint-manager.ts +28 -8
  218. package/src/mcpl/errors.ts +7 -0
  219. package/src/mcpl/feature-set-manager.ts +50 -5
  220. package/src/mcpl/hook-orchestrator.ts +64 -86
  221. package/src/mcpl/index.ts +0 -2
  222. package/src/mcpl/prose-stream-router.ts +17 -0
  223. package/src/mcpl/push-handler.ts +18 -2
  224. package/src/mcpl/server-connection.ts +262 -34
  225. package/src/mcpl/server-registry.ts +4 -3
  226. package/src/mcpl/tags.ts +50 -0
  227. package/src/mcpl/transport.ts +37 -7
  228. package/src/mcpl/types.ts +187 -78
  229. package/src/module-registry.ts +34 -0
  230. package/src/modules/workspace/index.ts +49 -15
  231. package/src/modules/workspace/sync.ts +1 -1
  232. package/src/refusal-reactions.ts +37 -0
  233. package/src/tool-result-history.ts +17 -5
  234. package/src/types/agent.ts +36 -0
  235. package/src/types/framework.ts +27 -0
  236. package/src/types/module.ts +24 -0
  237. package/src/types/trace.ts +22 -0
@@ -12,17 +12,30 @@
12
12
  * same message shape the managed runtime produces
13
13
  * - interpreter globals persist across execs (container-reuse semantics)
14
14
  *
15
+ * BACKGROUND MODE (model-authored daemons): when init carries
16
+ * `background: true`, the exec additionally gets:
17
+ * - `wake_agent(payload)` — async; reports the caller's line number (which
18
+ * indexes into the agent-authored script — self-correlation) and delivers
19
+ * the payload to the host, which injects it into the agent's context and
20
+ * triggers inference. Awaits a host ack so rate limiting backpressures
21
+ * inside the script instead of dropping wakes.
22
+ * - stdout/stderr tee'd line-buffered to `log_path` (agent-readable via
23
+ * their file tools) with an in-memory tail kept for the crash envelope.
24
+ *
15
25
  * Embedded as a TS string because tsc does not copy non-TS assets to dist/;
16
26
  * PyRunner materializes it to a temp file at spawn.
17
27
  *
18
28
  * Protocol (JSON lines):
19
- * host -> py : {op:"init", tools:[{py_name,tool_name}], call_timeout_s}
29
+ * host -> py : {op:"init", tools:[{py_name,tool_name}], call_timeout_s,
30
+ * background?, log_path?, tail_chars?}
20
31
  * {op:"exec", id, code}
21
32
  * {op:"tool_result", id, result}
33
+ * {op:"wake_ack", id, error?}
22
34
  * {op:"cancel", id, reason?}
23
35
  * py -> host : {op:"ready"}
24
36
  * {op:"tool_call", id, exec_id, name, args}
25
- * {op:"exec_result", id, stdout, stderr, return_code}
37
+ * {op:"wake", id, exec_id, line, payload}
38
+ * {op:"exec_result", id, stdout, stderr, return_code, tail?}
26
39
  *
27
40
  * IMPORTANT: the python source below must not contain backticks or the
28
41
  * sequence dollar+brace (TS template literal syntax). String.raw preserves
@@ -34,6 +47,7 @@ import asyncio
34
47
  import inspect
35
48
  import io
36
49
  import json
50
+ import os
37
51
  import sys
38
52
  import threading
39
53
  import traceback
@@ -42,6 +56,11 @@ PROTO_OUT = sys.stdout # protocol channel (real stdout)
42
56
  REAL_STDERR = sys.stderr # runtime diagnostics outside execs
43
57
  CALL_TIMEOUT_S = 270.0
44
58
  OUTPUT_CAP_CHARS = 2_000_000
59
+ LOG_FILE_CAP_BYTES = 50_000_000
60
+
61
+ BACKGROUND = False
62
+ LOG_PATH = None
63
+ TAIL_CHARS = 4000
45
64
 
46
65
  _send_lock = threading.Lock()
47
66
 
@@ -74,13 +93,78 @@ class CappedIO(io.StringIO):
74
93
  return super().write(s)
75
94
 
76
95
 
96
+ class LogTee:
97
+ """Background-mode writer: line-buffered file append + rolling tail.
98
+
99
+ The file is the agent-readable journal (their read/grep/shell tools work
100
+ on it); the tail rides the exec_result so the host can build a crash
101
+ envelope without re-reading the file.
102
+ """
103
+
104
+ def __init__(self, path, tail_chars):
105
+ self._fh = None
106
+ self._written = 0
107
+ self._tail = ""
108
+ self._tail_chars = tail_chars
109
+ self.truncated = False
110
+ if path:
111
+ try:
112
+ os.makedirs(os.path.dirname(path), exist_ok=True)
113
+ self._fh = open(path, "a", encoding="utf-8", errors="replace")
114
+ except Exception as exc:
115
+ REAL_STDERR.write("[pytc-runtime] cannot open log file: " + str(exc) + "\n")
116
+ REAL_STDERR.flush()
117
+
118
+ def write(self, s):
119
+ if not isinstance(s, str):
120
+ s = str(s)
121
+ self._tail = (self._tail + s)[-self._tail_chars:]
122
+ if self._fh is not None and not self.truncated:
123
+ if self._written + len(s) > LOG_FILE_CAP_BYTES:
124
+ self.truncated = True
125
+ try:
126
+ self._fh.write("\n[log capped at " + str(LOG_FILE_CAP_BYTES) + " bytes]\n")
127
+ self._fh.flush()
128
+ except Exception:
129
+ pass
130
+ else:
131
+ try:
132
+ self._fh.write(s)
133
+ if "\n" in s:
134
+ self._fh.flush()
135
+ self._written += len(s)
136
+ except Exception:
137
+ pass
138
+ return len(s)
139
+
140
+ def flush(self):
141
+ if self._fh is not None:
142
+ try:
143
+ self._fh.flush()
144
+ except Exception:
145
+ pass
146
+
147
+ def close(self):
148
+ if self._fh is not None:
149
+ try:
150
+ self._fh.close()
151
+ except Exception:
152
+ pass
153
+ self._fh = None
154
+
155
+ def tail(self):
156
+ return self._tail
157
+
158
+
77
159
  # Persistent interpreter state (mirrors container reuse: variables survive
78
160
  # between exec calls until the host reclaims the interpreter).
79
161
  SCRIPT_GLOBALS = {"__name__": "__main__", "__builtins__": __builtins__}
80
162
  _injected_py_names = set()
81
163
 
82
164
  _pending_tool_futures = {}
165
+ _pending_wake_futures = {}
83
166
  _next_tool_call_id = 0
167
+ _next_wake_id = 0
84
168
  _current_exec_id = None
85
169
  _current_exec_task = None
86
170
 
@@ -115,7 +199,6 @@ def _make_tool_fn(tool_name, py_name):
115
199
  )
116
200
  finally:
117
201
  _pending_tool_futures.pop(call_id, None)
118
-
119
202
  tool_fn.__name__ = py_name
120
203
  tool_fn.__qualname__ = py_name
121
204
  tool_fn.__doc__ = (
@@ -125,11 +208,47 @@ def _make_tool_fn(tool_name, py_name):
125
208
  return tool_fn
126
209
 
127
210
 
211
+ async def wake_agent(payload=None):
212
+ """Wake the agent: deliver payload into their context and trigger a turn.
213
+
214
+ Reports the caller's line number (indexes into the script the agent
215
+ wrote). Awaits the host ack — rate limiting backpressures here rather
216
+ than dropping wakes. Raises RuntimeError when the host refuses (wake cap
217
+ exceeded, script cancelled).
218
+ """
219
+ global _next_wake_id
220
+ frame = inspect.currentframe()
221
+ line = frame.f_back.f_lineno if frame is not None and frame.f_back is not None else -1
222
+ _next_wake_id += 1
223
+ wake_id = "w" + str(_next_wake_id)
224
+ fut = asyncio.get_running_loop().create_future()
225
+ _pending_wake_futures[wake_id] = fut
226
+ send({
227
+ "op": "wake",
228
+ "id": wake_id,
229
+ "exec_id": _current_exec_id,
230
+ "line": line,
231
+ "payload": payload,
232
+ })
233
+ try:
234
+ error = await fut
235
+ if error:
236
+ raise RuntimeError("wake_agent refused by host: " + str(error))
237
+ return None
238
+ finally:
239
+ _pending_wake_futures.pop(wake_id, None)
240
+
241
+
128
242
  def handle_init(msg):
129
- global CALL_TIMEOUT_S
243
+ global CALL_TIMEOUT_S, BACKGROUND, LOG_PATH, TAIL_CHARS
130
244
  timeout = msg.get("call_timeout_s")
131
245
  if isinstance(timeout, (int, float)) and timeout > 0:
132
246
  CALL_TIMEOUT_S = float(timeout)
247
+ BACKGROUND = bool(msg.get("background"))
248
+ LOG_PATH = msg.get("log_path") or None
249
+ tail = msg.get("tail_chars")
250
+ if isinstance(tail, int) and tail > 0:
251
+ TAIL_CHARS = tail
133
252
 
134
253
  # Remove functions injected by a previous init that are no longer present
135
254
  # (tool list changed) without touching user-defined globals.
@@ -151,12 +270,22 @@ def handle_init(msg):
151
270
  _injected_py_names.update(new_py_names)
152
271
  # Exact-name escape hatch: tools["mcpl--server--tool"]({...})
153
272
  SCRIPT_GLOBALS["tools"] = tools_dict
273
+ # The doorbell exists only in background mode — a foreground script's
274
+ # agent is already awake (mid-turn).
275
+ if BACKGROUND:
276
+ SCRIPT_GLOBALS["wake_agent"] = wake_agent
277
+ else:
278
+ SCRIPT_GLOBALS.pop("wake_agent", None)
154
279
 
155
280
 
156
281
  async def _run_script(exec_id, code):
157
282
  global _current_exec_id, _current_exec_task
158
- out = CappedIO(OUTPUT_CAP_CHARS)
159
- err = CappedIO(OUTPUT_CAP_CHARS)
283
+ if BACKGROUND:
284
+ out = LogTee(LOG_PATH, TAIL_CHARS)
285
+ err = out # interleave, terminal-style; tail is shared
286
+ else:
287
+ out = CappedIO(OUTPUT_CAP_CHARS)
288
+ err = CappedIO(OUTPUT_CAP_CHARS)
160
289
  old_out, old_err, old_in = sys.stdout, sys.stderr, sys.stdin
161
290
  sys.stdout, sys.stderr = out, err
162
291
  sys.stdin = io.StringIO("") # input() must not steal the protocol channel
@@ -176,11 +305,27 @@ async def _run_script(exec_id, code):
176
305
  sys.stdout, sys.stderr, sys.stdin = old_out, old_err, old_in
177
306
  _current_exec_id = None
178
307
  _current_exec_task = None
179
- # Fail any tool futures still pending: their exec is gone.
180
308
  for fut in list(_pending_tool_futures.values()):
181
309
  if not fut.done():
182
310
  fut.cancel()
183
311
  _pending_tool_futures.clear()
312
+ for fut in list(_pending_wake_futures.values()):
313
+ if not fut.done():
314
+ fut.cancel()
315
+ _pending_wake_futures.clear()
316
+
317
+ if BACKGROUND:
318
+ out.flush()
319
+ out.close()
320
+ send({
321
+ "op": "exec_result",
322
+ "id": exec_id,
323
+ "stdout": "",
324
+ "stderr": "",
325
+ "tail": out.tail(),
326
+ "return_code": return_code,
327
+ })
328
+ return
184
329
 
185
330
  stdout_text = out.getvalue()
186
331
  if out.truncated:
@@ -244,6 +389,10 @@ async def main():
244
389
  fut = _pending_tool_futures.get(msg.get("id"))
245
390
  if fut is not None and not fut.done():
246
391
  fut.set_result(str(msg.get("result", "")))
392
+ elif op == "wake_ack":
393
+ fut = _pending_wake_futures.get(msg.get("id"))
394
+ if fut is not None and not fut.done():
395
+ fut.set_result(msg.get("error") or None)
247
396
  elif op == "cancel":
248
397
  if _current_exec_task is not None and not _current_exec_task.done():
249
398
  _current_exec_task.cancel()
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-py.js","sourceRoot":"","sources":["../../../src/code-execution/runtime-py.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAW,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyOtD,CAAC"}
1
+ {"version":3,"file":"runtime-py.js","sourceRoot":"","sources":["../../../src/code-execution/runtime-py.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAW,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiXtD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-definition.d.ts","sourceRoot":"","sources":["../../../src/code-execution/tool-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,wBAAgB,gCAAgC,CAAC,IAAI,CAAC,EAAE;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,cAAc,CAoCjB"}
1
+ {"version":3,"file":"tool-definition.d.ts","sourceRoot":"","sources":["../../../src/code-execution/tool-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,wBAAgB,gCAAgC,CAAC,IAAI,CAAC,EAAE;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,cAAc,CA6DjB"}
@@ -31,7 +31,19 @@ export function buildCodeExecutionToolDefinition(opts) {
31
31
  'Use this when fanning out across many items, looping over tool calls, or when tool ' +
32
32
  'results are large and you only need a slice or summary. Call tools directly (not via ' +
33
33
  'code) when a single call answers the question or when you need to reason about each ' +
34
- 'result before deciding the next step.',
34
+ 'result before deciding the next step. ' +
35
+ 'BACKGROUND MODE: pass background=true to run the script as a detached watcher that ' +
36
+ 'outlives this turn — the tool returns immediately with a script_id and you can end ' +
37
+ 'your turn (e.g. sleep). Inside a background script, await wake_agent(payload) wakes ' +
38
+ 'you: the payload plus provenance (script id, the line number in your script, elapsed ' +
39
+ 'time) is delivered into your context and starts a turn for you. A script that ends ' +
40
+ 'without calling wake_agent wakes nobody — that silence is the point (poll cheaply, ' +
41
+ 'wake only on signal). If your background script CRASHES you are woken with the error. ' +
42
+ 'Its print() output streams to a workspace log file you can read any time. Wakes are ' +
43
+ 'rate-limited (early wakes are delayed, not dropped) and capped per script. ' +
44
+ 'CAUTION: background scripts die silently if the host process restarts — for a wake ' +
45
+ 'you absolutely must not miss, also arm a wake rule as backup. ' +
46
+ 'Manage your scripts with {"action": "list"} and {"action": "cancel", "script_id": "..."}.',
35
47
  inputSchema: {
36
48
  type: 'object',
37
49
  properties: {
@@ -39,8 +51,21 @@ export function buildCodeExecutionToolDefinition(opts) {
39
51
  type: 'string',
40
52
  description: 'Python code to execute. Top-level await is allowed.',
41
53
  },
54
+ background: {
55
+ type: 'boolean',
56
+ description: 'Run detached as a background watcher with wake_agent() available (default false).',
57
+ },
58
+ action: {
59
+ type: 'string',
60
+ enum: ['run', 'list', 'cancel'],
61
+ description: 'run (default) executes `code`; list shows your background scripts; cancel stops one.',
62
+ },
63
+ script_id: {
64
+ type: 'string',
65
+ description: 'Background script id (for action: cancel).',
66
+ },
42
67
  },
43
- required: ['code'],
68
+ required: [],
44
69
  },
45
70
  };
46
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-definition.js","sourceRoot":"","sources":["../../../src/code-execution/tool-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,CAAC,MAAM,wBAAwB,GAAG,gBAAgB,CAAC;AAEzD,MAAM,UAAU,gCAAgC,CAAC,IAGhD;IACC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,aAAa,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IACvF,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,iBAAiB,IAAI,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACnF,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACT,mEAAmE;YACnE,kFAAkF;YAClF,8EAA8E;YAC9E,2CAA2C;YAC3C,0FAA0F;YAC1F,oEAAoE;YACpE,sFAAsF;YACtF,0FAA0F;YAC1F,6FAA6F;YAC7F,8EAA8E;YAC9E,yFAAyF;YACzF,0FAA0F;YAC1F,uFAAuF;YACvF,iCAAiC,WAAW,8CAA8C;YAC1F,oBAAoB,kBAAkB,2CAA2C;YACjF,qFAAqF;YACrF,uFAAuF;YACvF,sFAAsF;YACtF,uCAAuC;QACzC,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,qDAAqD;iBACnE;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tool-definition.js","sourceRoot":"","sources":["../../../src/code-execution/tool-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,CAAC,MAAM,wBAAwB,GAAG,gBAAgB,CAAC;AAEzD,MAAM,UAAU,gCAAgC,CAAC,IAGhD;IACC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,aAAa,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IACvF,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,iBAAiB,IAAI,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACnF,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACT,mEAAmE;YACnE,kFAAkF;YAClF,8EAA8E;YAC9E,2CAA2C;YAC3C,0FAA0F;YAC1F,oEAAoE;YACpE,sFAAsF;YACtF,0FAA0F;YAC1F,6FAA6F;YAC7F,8EAA8E;YAC9E,yFAAyF;YACzF,0FAA0F;YAC1F,uFAAuF;YACvF,iCAAiC,WAAW,8CAA8C;YAC1F,oBAAoB,kBAAkB,2CAA2C;YACjF,qFAAqF;YACrF,uFAAuF;YACvF,sFAAsF;YACtF,wCAAwC;YACxC,qFAAqF;YACrF,qFAAqF;YACrF,sFAAsF;YACtF,uFAAuF;YACvF,qFAAqF;YACrF,qFAAqF;YACrF,wFAAwF;YACxF,sFAAsF;YACtF,6EAA6E;YAC7E,qFAAqF;YACrF,gEAAgE;YAChE,2FAA2F;QAC7F,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,qDAAqD;iBACnE;gBACD,UAAU,EAAE;oBACV,IAAI,EAAE,SAAS;oBACf,WAAW,EAAE,mFAAmF;iBACjG;gBACD,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC;oBAC/B,WAAW,EAAE,sFAAsF;iBACpG;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,4CAA4C;iBAC1D;aACF;YACD,QAAQ,EAAE,EAAE;SACb;KACF,CAAC;AACJ,CAAC"}