@flowriki/agent 0.1.0-beta.57 → 0.1.0-beta.58

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.
@@ -76,7 +76,7 @@ import os from "os";
76
76
  import path from "path";
77
77
  import { createRequire } from "module";
78
78
  import { spawnSync } from "child_process";
79
- var AGENT_VERSION = "0.1.0-beta.57";
79
+ var AGENT_VERSION = "0.1.0-beta.58";
80
80
  async function prepareBrowsers() {
81
81
  if (browsersReady()) {
82
82
  return { ok: true, already: true, message: "chromium is already installed" };
@@ -9806,6 +9806,8 @@ function assistantHandlers({ deps }) {
9806
9806
  ...linked.length ? { folders: linked } : {},
9807
9807
  ...mode ? { mode } : {},
9808
9808
  ...brief ? { brief } : {},
9809
+ ...command.args["platform"] === "web" || command.args["platform"] === "android" || command.args["platform"] === "ios" ? { platform: command.args["platform"] } : {},
9810
+ ...command.args["reuse"] === false ? { reuse: false } : {},
9809
9811
  ...typeof sessionId === "string" ? { sessionId } : {},
9810
9812
  emit
9811
9813
  });
@@ -10175,7 +10177,8 @@ function buildAskArgs(opts) {
10175
10177
  const brief = opts.brief?.trim();
10176
10178
  const guidance = [
10177
10179
  `You are the Flowriki assistant for the project "${opts.projectName}" (id ${opts.projectId}). Use the Flowriki tools, and pass projectId "${opts.projectId}" wherever a tool takes one. ` + (brief ? "The project brief below is this project's memory for this job: read it first." : "Start with project_brief: it is this project's memory."),
10178
- ask ? "ASK MODE: answer the question; change nothing. Cite what you rely on as [[testCase:<its id, tc_\u2026>]], [[knowledge:features/checkout.md]] or [[file:path/in/repo.ts]]." : "DO MODE: do what is asked, on Claude's working branch, for a person to review. Ask in the chat and wait before publishing, deleting, or sending anything to another system. End by writing a short summary of what you did with write_knowledge (memory/sessions/<date>-<topic>.md, append) so the next session knows.",
10180
+ ask ? "ASK MODE: answer the question; change nothing. Cite what you rely on as [[testCase:<its id, tc_\u2026>]], [[knowledge:features/checkout.md]] or [[file:path/in/repo.ts]]." : "DO MODE: do what is asked, on Claude's working branch, for a person to review. Ask in the chat and wait before publishing, deleting, or sending anything to another system. Work quietly: the person sees what you build and your final answer, so keep words between tool calls to a minimum and end with a short result \u2014 what was saved, and anything that needs them. When you saved something, write a short summary with write_knowledge (memory/sessions/<date>-<topic>.md, append) so the next session knows.",
10181
+ "Use only the tools you have: a tool is used by calling it, not by writing the call out as text. If Flowriki's tools are missing, say so in one sentence and stop.",
10179
10182
  folders.length ? `The project's code is in ${folders.join(" and ")}. Read it with Read, Grep and Glob when it helps; you cannot change it.` : "",
10180
10183
  brief ? `
10181
10184
  ${brief}` : ""
@@ -10215,8 +10218,10 @@ function eventsFromLine(askId, line) {
10215
10218
  return [];
10216
10219
  }
10217
10220
  const out = [];
10218
- if (parsed.type === "system" && parsed.subtype === "init" && parsed.session_id) {
10219
- out.push({ name: "assistant.session", data: { askId, sessionId: parsed.session_id } });
10221
+ if (parsed.type === "system" && parsed.subtype === "init") {
10222
+ const ours = (parsed.mcp_servers ?? []).find((s2) => s2.name === MCP_SERVER_NAME);
10223
+ if (ours) out.push({ name: "assistant.tools", data: { askId, status: ours.status ?? "unknown" } });
10224
+ if (parsed.session_id) out.push({ name: "assistant.session", data: { askId, sessionId: parsed.session_id } });
10220
10225
  }
10221
10226
  if (parsed.type === "assistant") {
10222
10227
  for (const block of parsed.message?.content ?? []) {
@@ -10299,6 +10304,35 @@ function createClaudeCli(options) {
10299
10304
  child.on("close", (code) => finish(code, false));
10300
10305
  });
10301
10306
  }
10307
+ const LANE_FRESH_MS = 60 * 6e4;
10308
+ const LANE_MAX_TOKENS = 15e4;
10309
+ const laneFile = (dir) => path42.join(dir, "lane-sessions.json");
10310
+ const readLanes = (dir) => {
10311
+ try {
10312
+ return JSON.parse(fs43.readFileSync(laneFile(dir), "utf8"));
10313
+ } catch {
10314
+ return {};
10315
+ }
10316
+ };
10317
+ const writeLane = (dir, platform, record) => {
10318
+ const all = readLanes(dir);
10319
+ if (record) all[platform] = record;
10320
+ else delete all[platform];
10321
+ try {
10322
+ fs43.writeFileSync(laneFile(dir), JSON.stringify(all), { mode: 384 });
10323
+ } catch {
10324
+ }
10325
+ };
10326
+ function laneSession(ask, dir) {
10327
+ if (ask.sessionId || !ask.platform) return ask.sessionId;
10328
+ if (ask.reuse === false) {
10329
+ writeLane(dir, ask.platform, null);
10330
+ return void 0;
10331
+ }
10332
+ const kept = readLanes(dir)[ask.platform];
10333
+ if (!kept || Date.now() - kept.updatedAt > LANE_FRESH_MS || kept.tokens > LANE_MAX_TOKENS) return void 0;
10334
+ return kept.sessionId;
10335
+ }
10302
10336
  return {
10303
10337
  async status() {
10304
10338
  const version = await capture(["--version"]);
@@ -10350,7 +10384,7 @@ function createClaudeCli(options) {
10350
10384
  JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: { command: options.mcp.command, args: options.mcp.args } } }),
10351
10385
  { mode: 384 }
10352
10386
  );
10353
- return launch(ask, dir, mcpConfigPath, ask.sessionId);
10387
+ return launch(ask, dir, mcpConfigPath, laneSession(ask, dir));
10354
10388
  },
10355
10389
  cancel(askId) {
10356
10390
  const child = running.get(askId);
@@ -10366,7 +10400,7 @@ function createClaudeCli(options) {
10366
10400
  return running.size >= MAX_AT_ONCE;
10367
10401
  }
10368
10402
  };
10369
- function launch(ask, dir, mcpConfigPath, sessionId) {
10403
+ function launch(ask, dir, mcpConfigPath, sessionId, attempt = 1) {
10370
10404
  let child;
10371
10405
  try {
10372
10406
  child = spawn2(
@@ -10390,8 +10424,21 @@ function createClaudeCli(options) {
10390
10424
  running.set(ask.askId, child);
10391
10425
  let buffer = "", stderr = "", sawDone = false, ended = false;
10392
10426
  let silentFailure = false;
10427
+ let toolsMissing = false;
10428
+ let session;
10429
+ let spent = 0;
10393
10430
  const handleLine = (line) => {
10431
+ if (toolsMissing) return;
10394
10432
  for (const event of eventsFromLine(ask.askId, line)) {
10433
+ if (event.name === "assistant.tools") {
10434
+ if (event.data["status"] !== "connected") {
10435
+ toolsMissing = true;
10436
+ child.kill("SIGTERM");
10437
+ return;
10438
+ }
10439
+ continue;
10440
+ }
10441
+ if (event.name === "assistant.session") session = String(event.data["sessionId"] ?? "") || session;
10395
10442
  if (event.name === "assistant.done" && event.data["ok"] === false && !String(event.data["result"] ?? "").trim()) {
10396
10443
  silentFailure = true;
10397
10444
  continue;
@@ -10399,6 +10446,14 @@ function createClaudeCli(options) {
10399
10446
  if (event.name === "assistant.done") sawDone = true;
10400
10447
  ask.emit(event.name, event.data);
10401
10448
  }
10449
+ try {
10450
+ const parsed = JSON.parse(line);
10451
+ if (parsed.type === "result" && parsed.usage) {
10452
+ const u = parsed.usage;
10453
+ spent += (u.input_tokens ?? 0) + (u.output_tokens ?? 0) + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
10454
+ }
10455
+ } catch {
10456
+ }
10402
10457
  };
10403
10458
  const end = (ok, detail) => {
10404
10459
  if (ended) return;
@@ -10406,10 +10461,26 @@ function createClaudeCli(options) {
10406
10461
  clearTimeout(timer);
10407
10462
  if (buffer.trim()) handleLine(buffer);
10408
10463
  running.delete(ask.askId);
10464
+ if (toolsMissing && cancelled !== ask.askId) {
10465
+ if (attempt < 3) {
10466
+ setTimeout(() => {
10467
+ const again = launch(ask, dir, mcpConfigPath, sessionId, attempt + 1);
10468
+ if (!again.started) ask.emit("assistant.done", { askId: ask.askId, ok: false, result: again.detail ?? "The assistant could not be started." });
10469
+ }, 1500 * attempt + Math.floor(Math.random() * 1e3)).unref?.();
10470
+ return;
10471
+ }
10472
+ ask.emit("assistant.done", { askId: ask.askId, ok: false, result: "Flowriki's tools did not connect to the assistant on this computer after three tries, so nothing was done. Send it again in a moment; if it keeps happening, restart the agent." });
10473
+ return;
10474
+ }
10409
10475
  if (silentFailure && sessionId && /no conversation found/i.test(stderr) && cancelled !== ask.askId) {
10476
+ if (ask.platform) writeLane(dir, ask.platform, null);
10410
10477
  const again = launch(ask, dir, mcpConfigPath, void 0);
10411
10478
  if (again.started) return;
10412
10479
  }
10480
+ if (ask.platform && session && ok && !silentFailure) {
10481
+ const before = sessionId && readLanes(dir)[ask.platform]?.sessionId === sessionId ? readLanes(dir)[ask.platform].tokens : 0;
10482
+ writeLane(dir, ask.platform, { sessionId: session, updatedAt: Date.now(), tokens: Math.max(before, spent) });
10483
+ }
10413
10484
  if (!sawDone) {
10414
10485
  const why = detail || stderr.trim().split("\n").filter((l) => l && !/^Warning:/.test(l)).slice(-3).join(" ") || "The assistant stopped without saying why.";
10415
10486
  ask.emit("assistant.done", { askId: ask.askId, ok: ok && !silentFailure, result: ok && !silentFailure ? "" : why });
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  runtimeValue,
25
25
  start,
26
26
  status
27
- } from "./chunk-JFMQJDBI.js";
27
+ } from "./chunk-RLRHWYSJ.js";
28
28
  import {
29
29
  CLOUD_PROVIDERS,
30
30
  CLOUD_PROVIDER_NAME,
package/dist/engine.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveCheckouts,
10
10
  start,
11
11
  status
12
- } from "./chunk-JFMQJDBI.js";
12
+ } from "./chunk-RLRHWYSJ.js";
13
13
  import "./chunk-O34WXNH6.js";
14
14
  export {
15
15
  AGENT_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowriki/agent",
3
- "version": "0.1.0-beta.57",
3
+ "version": "0.1.0-beta.58",
4
4
  "description": "Run Flowriki UI tests on your own computer. Connects this machine to your Flowriki account.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",