@q-agent/agent 0.1.25 → 0.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/src/api.js CHANGED
@@ -58,6 +58,7 @@ exports.postExploreFinalize = postExploreFinalize;
58
58
  exports.postComplete = postComplete;
59
59
  exports.claimNextAuthoring = claimNextAuthoring;
60
60
  exports.postAuthoringEvent = postAuthoringEvent;
61
+ exports.postAuthoringEventAlive = postAuthoringEventAlive;
61
62
  exports.postAuthoringFinalize = postAuthoringFinalize;
62
63
  const fs = __importStar(require("fs"));
63
64
  /** Raised for any non-2xx/204 response; carries the HTTP status for callers that care (e.g. 409). */
@@ -311,6 +312,26 @@ async function postAuthoringEvent(cfg, sessionId, event, payload) {
311
312
  });
312
313
  await throwIfNotOk(res);
313
314
  }
315
+ /**
316
+ * Post an authoring progress event and report whether the session is still alive
317
+ * (#420). Returns false on a 404 — the server purged the session because the run
318
+ * was stopped — which the caller uses as a mid-flight abort signal to kill the
319
+ * local Claude run. Never throws: a transient network error returns true so a
320
+ * blip doesn't abort a live session.
321
+ */
322
+ async function postAuthoringEventAlive(cfg, sessionId, event, payload) {
323
+ try {
324
+ const res = await fetch(`${cfg.serverUrl}/agent/authoring/${sessionId}/events`, {
325
+ method: "POST",
326
+ headers: { ...authHeaders(cfg.deviceToken), "Content-Type": "application/json" },
327
+ body: JSON.stringify({ event, payload }),
328
+ });
329
+ return res.status !== 404;
330
+ }
331
+ catch {
332
+ return true;
333
+ }
334
+ }
314
335
  /** Finalize an authoring session: the server gates + persists the authored spec
315
336
  * and KB-merges the runtime-verified discovery. */
316
337
  async function postAuthoringFinalize(cfg, sessionId, body) {
@@ -1095,16 +1095,36 @@ async function processAuthoringJob(cfg, job) {
1095
1095
  });
1096
1096
  activeChild = claude;
1097
1097
  // Surface each step to the agent console + the run WebSocket so the operator
1098
- // can watch Claude drive browser-harness live.
1098
+ // can watch Claude drive browser-harness live. Each post also reports whether
1099
+ // the session is still alive: a 404 means the run was stopped server-side
1100
+ // (#420), so abort the local Claude run immediately instead of burning the
1101
+ // rest of the budget on work whose result will be rejected anyway.
1102
+ let aborted = false;
1103
+ // AGENT LOG mirrors the web trail's Settings verbosity (#438): in "concise"
1104
+ // (default) skip the raw tool/Bash step lines (▷ …) so the local log shows
1105
+ // only Claude's readable narration. The server WS still gets every line — the
1106
+ // web filters its own stream — and abort-on-stop keys on those posts.
1107
+ const conciseLog = (job.logVerbosity ?? "concise") === "concise";
1099
1108
  const emitStep = (line) => {
1100
1109
  const trimmed = line.length > 300 ? line.slice(0, 300) + "…" : line;
1101
1110
  console.log(`[authoring ${job.caseId}] ${trimmed}`);
1102
- (0, bus_1.emit)("authoring-step", { caseId: job.caseId, line: trimmed });
1111
+ if (!(conciseLog && trimmed.trimStart().startsWith(""))) {
1112
+ (0, bus_1.emit)("authoring-step", { caseId: job.caseId, line: trimmed });
1113
+ }
1103
1114
  void api
1104
- .postAuthoringEvent(cfg, job.sessionId, "authoring.progress", {
1115
+ .postAuthoringEventAlive(cfg, job.sessionId, "authoring.progress", {
1105
1116
  case: job.caseId, phase: "step", message: trimmed,
1106
1117
  })
1107
- .catch(() => { });
1118
+ .then((alive) => {
1119
+ if (!alive && !aborted) {
1120
+ aborted = true;
1121
+ console.log(`[authoring ${job.caseId}] session gone — run stopped; aborting Claude`);
1122
+ try {
1123
+ claude?.kill();
1124
+ }
1125
+ catch { /* already exited */ }
1126
+ }
1127
+ });
1108
1128
  };
1109
1129
  let cerr = "";
1110
1130
  let finalResult = "";
package/dist/src/ui.js CHANGED
@@ -497,12 +497,14 @@ function renderCells(){
497
497
  }
498
498
 
499
499
  function timeStr(ts){ return new Date(ts).toTimeString().slice(0,8); }
500
+ var LOG_EMPTY_HTML = '<div id="logEmptyPh" style="color:#5c5c6e;padding:2px 0;line-height:1.7">Ready \\u00b7 waiting for activity\\u2026<br><span style="color:#4c4c5a;font-size:10.5px">Runs, live-authoring and self-heal will stream here.</span></div>';
500
501
  function logColor(t){
501
502
  if (t === "error" || t === "auth-error") return "#ff9ea1";
502
503
  if (t === "auth-waiting") return "#fbbf24";
503
504
  if (t === "auth-captured") return "#6ee7b7";
504
505
  if (t === "job-claimed") return "#c4b5fd";
505
506
  if (t === "job-complete") return "#6ee7b7";
507
+ if (t === "authoring-step") return "#c3c3d0";
506
508
  return "#8b8b9e";
507
509
  }
508
510
  function logMsg(ev){
@@ -516,12 +518,14 @@ function logMsg(ev){
516
518
  case "job-complete": return "execution #" + ev.executionId + " complete \\u00b7 " + ev.passed + " passed, " + ev.failed + " failed";
517
519
  case "error": return ev.message || "error";
518
520
  case "log": return ev.message || "";
521
+ case "authoring-step": return ev.line || null; // live authoring / self-heal steps (#400/#428)
519
522
  default: return null;
520
523
  }
521
524
  }
522
525
  function addLog(ev){
523
526
  if (ev.type === "agent-status") return; // status events aren't log lines; pill is driven by view state
524
527
  var msg = logMsg(ev); if (!msg) return;
528
+ var ph = document.getElementById("logEmptyPh"); if (ph) ph.remove(); // first real line clears the placeholder
525
529
  var row = document.createElement("div");
526
530
  row.style.cssText = "display:flex;gap:10px;animation:logIn .4s ease both";
527
531
  var t = document.createElement("span"); t.style.cssText = "color:#5c5c6e;flex-shrink:0"; t.textContent = timeStr(ev.ts);
@@ -580,7 +584,9 @@ function showConnected(s){
580
584
  el("sessionId").textContent = "device #" + (s.deviceId==null?"?":s.deviceId);
581
585
  // The SSE stream replays the recent-events buffer on connect, so seed the log
582
586
  // ONLY from the stream (seeding from /api/state too would double every line).
583
- el("log").innerHTML = ""; openStream();
587
+ // Show a placeholder until the first line arrives so an idle/just-reconnected
588
+ // agent reads as "ready & waiting", not a broken empty panel (#agent-log-empty).
589
+ el("log").innerHTML = LOG_EMPTY_HTML; openStream();
584
590
  }
585
591
  function refresh(){
586
592
  fetch("/api/state").then(function(r){ return r.json(); }).then(function(s){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q-agent/agent",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Q-Agent Local Agent — claims execution jobs from the Q-Agent server and runs Playwright locally, so manual login/MFA happens on the user's own machine and session credentials never leave it.",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {