@melaya/runner 1.1.25 → 1.1.27
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/dist/assistantHost.py +46 -3
- package/dist/browserGrantVerify.js +10 -4
- package/dist/connection.js +2 -2
- package/package.json +1 -1
package/dist/assistantHost.py
CHANGED
|
@@ -426,6 +426,39 @@ def _log(msg: str) -> None:
|
|
|
426
426
|
pass
|
|
427
427
|
|
|
428
428
|
|
|
429
|
+
# Failures the user can act on, told apart from the ones only we can. The runner
|
|
430
|
+
# already writes a full traceback to stderr; this builds the ONE line that reaches
|
|
431
|
+
# the panel, so it has to carry the exception type when the message is empty and
|
|
432
|
+
# stay readable when it is not.
|
|
433
|
+
_FRIENDLY_TURN_ERRORS = (
|
|
434
|
+
("timeout", "The model did not respond in time. Try again, or pick a different model."),
|
|
435
|
+
("timed out", "The model did not respond in time. Try again, or pick a different model."),
|
|
436
|
+
("connection", "Could not reach the model provider from this machine. Check the network and try again."),
|
|
437
|
+
("resolve", "Could not reach the model provider from this machine. Check the network and try again."),
|
|
438
|
+
("refused", "The model provider refused the connection from this machine."),
|
|
439
|
+
("ssl", "The connection to the model provider could not be secured on this machine."),
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _turn_error_message(exc: BaseException) -> str:
|
|
444
|
+
"""One line for the panel. Falls back to the exception TYPE, never to nothing."""
|
|
445
|
+
raw = str(exc).strip()
|
|
446
|
+
name = type(exc).__name__
|
|
447
|
+
probe = (raw or name).lower()
|
|
448
|
+
for needle, friendly in _FRIENDLY_TURN_ERRORS:
|
|
449
|
+
if needle in probe:
|
|
450
|
+
detail = ": " + raw[:200] if raw else ""
|
|
451
|
+
return friendly + " (" + name + detail + ")"
|
|
452
|
+
if raw:
|
|
453
|
+
return raw[:500]
|
|
454
|
+
# Empty message: the type is all there is, and it is genuinely useful -
|
|
455
|
+
# TimeoutError and ConnectionResetError point at different fixes.
|
|
456
|
+
return (
|
|
457
|
+
"The assistant turn failed with " + name + " and no further detail. "
|
|
458
|
+
"The full traceback is in the runner log."
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
429
462
|
def _extract_text(result) -> str:
|
|
430
463
|
"""Pull the final assistant text out of an agentscope reply Msg."""
|
|
431
464
|
if result is None:
|
|
@@ -907,7 +940,14 @@ def _run_turn(agent, turn_id: str, message: str, browser_turn: bool = False) ->
|
|
|
907
940
|
except Exception as exc:
|
|
908
941
|
_log("turn error:\n" + traceback.format_exc())
|
|
909
942
|
_stream["turnId"] = ""
|
|
910
|
-
|
|
943
|
+
# NEVER surface a bare "assistant turn failed". That fallback fired only
|
|
944
|
+
# when str(exc) was EMPTY - exactly the case where the exception TYPE is
|
|
945
|
+
# the sole remaining clue, and it was being discarded. Several of the
|
|
946
|
+
# likeliest failures here stringify to nothing at all (asyncio.TimeoutError,
|
|
947
|
+
# httpx.ReadTimeout, ConnectionResetError), so the panel showed a message
|
|
948
|
+
# that named neither what broke nor where. Reported 2026-09-03: two turns
|
|
949
|
+
# in a row with no cause anywhere on screen.
|
|
950
|
+
_emit(turn_id, "error", message=_turn_error_message(exc))
|
|
911
951
|
_emit(turn_id, "done")
|
|
912
952
|
return
|
|
913
953
|
|
|
@@ -921,7 +961,10 @@ def _run_turn(agent, turn_id: str, message: str, browser_turn: bool = False) ->
|
|
|
921
961
|
if result is _WALLCLOCK:
|
|
922
962
|
_stream["turnId"] = ""
|
|
923
963
|
_log("browser turn wall-clock budget exceeded")
|
|
924
|
-
_emit(turn_id, "error", message=
|
|
964
|
+
_emit(turn_id, "error", message=(
|
|
965
|
+
"This browser task ran past its time budget and was stopped. "
|
|
966
|
+
"Ask again with a narrower step, or split it into two."
|
|
967
|
+
))
|
|
925
968
|
_emit(turn_id, "done")
|
|
926
969
|
return
|
|
927
970
|
|
|
@@ -988,7 +1031,7 @@ def main() -> int:
|
|
|
988
1031
|
agent = _build_agent()
|
|
989
1032
|
except Exception as exc:
|
|
990
1033
|
_log("boot failed:\n" + traceback.format_exc())
|
|
991
|
-
_emit("", "error", message=
|
|
1034
|
+
_emit("", "error", message="The assistant could not start on this runner. " + _turn_error_message(exc))
|
|
992
1035
|
return 1
|
|
993
1036
|
# Capture the freshly-built BASE system prompt (before any static context is
|
|
994
1037
|
# folded in) so each turn can deterministically rebuild base + persona.
|
|
@@ -143,10 +143,16 @@ export function loadPinnedPublicKeysFromEnv() {
|
|
|
143
143
|
// Verifier
|
|
144
144
|
// ---------------------------------------------------------------------
|
|
145
145
|
export const DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
146
|
-
/** Sanity ceiling:
|
|
147
|
-
* token claiming a
|
|
148
|
-
*
|
|
149
|
-
|
|
146
|
+
/** Sanity ceiling: MUST match the server's MAX_TTL_SECONDS
|
|
147
|
+
* (server/src/services/browser/browserGrant.ts). A "valid" token claiming a
|
|
148
|
+
* longer life than the issuer can mint is forged or corrupt, so it is
|
|
149
|
+
* rejected regardless of signature.
|
|
150
|
+
*
|
|
151
|
+
* Raised 900s -> 4h alongside the server. Because this half ships in the
|
|
152
|
+
* runner, a server deployed ahead of a runner republish would mint grants
|
|
153
|
+
* this rejects with claims_invalid — publish the runner before, or at the
|
|
154
|
+
* same time as, the server change. */
|
|
155
|
+
const MAX_LIFETIME_SECONDS = 4 * 60 * 60;
|
|
150
156
|
function b64urlJson(part, what) {
|
|
151
157
|
let buf;
|
|
152
158
|
try {
|
package/dist/connection.js
CHANGED
|
@@ -1478,7 +1478,7 @@ export async function connect(opts) {
|
|
|
1478
1478
|
socket.on("runner:assistant_turn", async (payload) => {
|
|
1479
1479
|
const s = activeAssistants.get(String(payload.sessionId || ""));
|
|
1480
1480
|
if (!s) {
|
|
1481
|
-
socket.emit("runner:assistant_event", { sessionId: payload.sessionId, turnId: payload.turnId, kind: "error", message: "
|
|
1481
|
+
socket.emit("runner:assistant_event", { sessionId: payload.sessionId, turnId: payload.turnId, kind: "error", message: "The assistant session on your runner is gone (it most likely restarted). Send the message again to start a fresh one." });
|
|
1482
1482
|
return;
|
|
1483
1483
|
}
|
|
1484
1484
|
// Generation gate (defense-in-depth): refuse to serve a turn stamped with a
|
|
@@ -1487,7 +1487,7 @@ export async function connect(opts) {
|
|
|
1487
1487
|
// triggers the server's clean re-boot path. Backward-compatible: a turn with
|
|
1488
1488
|
// no generation (older server) is served as before.
|
|
1489
1489
|
if (payload.generation != null && Number(payload.generation) !== s.generation) {
|
|
1490
|
-
socket.emit("runner:assistant_event", { sessionId: payload.sessionId, turnId: payload.turnId, kind: "error", message: "
|
|
1490
|
+
socket.emit("runner:assistant_event", { sessionId: payload.sessionId, turnId: payload.turnId, kind: "error", message: "The assistant session on your runner is gone (it most likely restarted). Send the message again to start a fresh one." });
|
|
1491
1491
|
return;
|
|
1492
1492
|
}
|
|
1493
1493
|
s.lastActivity = Date.now();
|