@melaya/runner 1.0.87 → 1.0.89

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.
@@ -169,6 +169,11 @@ def _build_agent():
169
169
  # explodes context. Bounded to the selected services so the model can't reach
170
170
  # a connector the user didn't enable / has no creds for.
171
171
  connector_services = [s.strip().lower() for s in os.environ.get("MEL_ASSISTANT_CONNECTORS", "").split(",") if s.strip()]
172
+ # Self-arm the write-approval gate whenever connectors are active, so it can't
173
+ # be silently OFF on an older runner build that didn't set this env. Writes
174
+ # then ALWAYS require the in-chat approval card (fail-safe).
175
+ if connector_services and not os.environ.get("MEL_ASSISTANT_CONNECTOR_HITL"):
176
+ os.environ["MEL_ASSISTANT_CONNECTOR_HITL"] = "1"
172
177
  try:
173
178
  if connector_services:
174
179
  from shared.orchestration.lazy_registry import build_lazy_toolkit
@@ -251,15 +256,39 @@ def _build_agent():
251
256
  return agent
252
257
 
253
258
 
259
+ _HOOKS_REGISTERED = False
260
+
261
+
254
262
  def _register_stream_hooks(agent) -> None:
255
- """Emit delta / tool events live as the agent produces output (pre_print gives
256
- cumulative snapshots per message; we forward the new suffix as a delta)."""
263
+ """Emit delta / tool events live as the agent produces output.
264
+
265
+ Registers agentscope CLASS hooks on ReActAgent — NOT instance hooks on the
266
+ object make_agent returned. With reliability=True (and bounded_memory) that
267
+ object is a WRAPPER around the real ReActAgent, so an instance hook on it
268
+ never fires when the inner agent runs its acting / print steps: the FE then
269
+ only ever saw the `round` event ("thinking…") and never the tool / delta
270
+ stream. Class hooks fire for the inner agent regardless of wrapping — the
271
+ exact mechanism the pipeline event bus uses (shared.runtime.events
272
+ .wire_agentscope_hooks). There's one agent per host process, so a class hook
273
+ is effectively instance-scoped here; it keys output to the in-flight turn via
274
+ _stream and no-ops between turns (turnId == "").
275
+
276
+ pre_print gives cumulative snapshots per message; we forward the new suffix
277
+ as a delta. post_acting gives the tool-call batch; one `tool` event each.
278
+ """
279
+ global _HOOKS_REGISTERED
280
+ if _HOOKS_REGISTERED:
281
+ return
257
282
  try:
258
283
  from shared.runtime.events import _flatten_agent_text
259
284
  except Exception:
260
285
  return
286
+ try:
287
+ from agentscope.agent import ReActAgent
288
+ except Exception:
289
+ return
261
290
 
262
- def _pre_print(*_args, **kwargs) -> None:
291
+ def _pre_print_hook(_agent, **kwargs) -> None:
263
292
  try:
264
293
  tid = _stream.get("turnId")
265
294
  if not tid:
@@ -278,7 +307,7 @@ def _register_stream_hooks(agent) -> None:
278
307
  except Exception:
279
308
  pass
280
309
 
281
- def _post_acting(*_args, **kwargs) -> None:
310
+ def _post_acting_hook(_agent, **kwargs) -> None:
282
311
  try:
283
312
  tid = _stream.get("turnId")
284
313
  if not tid:
@@ -290,11 +319,30 @@ def _register_stream_hooks(agent) -> None:
290
319
  except Exception:
291
320
  pass
292
321
 
293
- for hook_type, fn in (("pre_print", _pre_print), ("post_acting", _post_acting)):
322
+ # Our agentscope's hook registration is 3-arg: (hook_type, hook_name, hook).
323
+ # The ORIGINAL code here called register_INSTANCE_hook(hook_type, fn) — 2 args
324
+ # against a 3-arg method AND on the reliability WRAPPER rather than the inner
325
+ # ReActAgent → TypeError, silently swallowed → the hooks were NEVER registered.
326
+ # That is why the FE only ever saw the `round` event ("thinking…") and none of
327
+ # the tool / delta stream. Register CLASS hooks with the correct arity (2-arg
328
+ # kept as a defensive fallback in case the signature ever changes).
329
+ def _reg(hook_type: str, name: str, fn) -> bool:
294
330
  try:
295
- agent.register_instance_hook(hook_type, fn)
331
+ ReActAgent.register_class_hook(hook_type, name, fn) # 3-arg (vendored)
332
+ return True
333
+ except TypeError:
334
+ try:
335
+ ReActAgent.register_class_hook(hook_type, fn) # 2-arg (pip build)
336
+ return True
337
+ except Exception:
338
+ return False
296
339
  except Exception:
297
- pass
340
+ return False
341
+
342
+ ok_a = _reg("pre_print", "mel_assistant_delta", _pre_print_hook)
343
+ ok_b = _reg("post_acting", "mel_assistant_tool", _post_acting_hook)
344
+ _HOOKS_REGISTERED = ok_a or ok_b
345
+ _log(f"stream hooks registered (delta={ok_a} tool={ok_b})")
298
346
 
299
347
 
300
348
  def _run_turn(agent, turn_id: str, message: str) -> None:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melaya/runner",
3
- "version": "1.0.87",
3
+ "version": "1.0.89",
4
4
  "description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,