@flowriki/agent 0.1.0-beta.60 → 0.1.0-beta.62
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.
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
textIndependent,
|
|
69
69
|
validateActors,
|
|
70
70
|
webCloudOf
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-YCFTNJQU.js";
|
|
72
72
|
|
|
73
73
|
// src/engine/machine.ts
|
|
74
74
|
import fs from "fs";
|
|
@@ -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.
|
|
79
|
+
var AGENT_VERSION = "0.1.0-beta.62";
|
|
80
80
|
async function prepareBrowsers() {
|
|
81
81
|
if (browsersReady()) {
|
|
82
82
|
return { ok: true, already: true, message: "chromium is already installed" };
|
|
@@ -136,7 +136,7 @@ async function checkMachine() {
|
|
|
136
136
|
}
|
|
137
137
|
} catch {
|
|
138
138
|
}
|
|
139
|
-
const { mobileDoctor: mobileDoctor2 } = await import("./dist-
|
|
139
|
+
const { mobileDoctor: mobileDoctor2 } = await import("./dist-EGC7VEBG.js");
|
|
140
140
|
return {
|
|
141
141
|
agentVersion: AGENT_VERSION,
|
|
142
142
|
node: process.versions.node,
|
|
@@ -3139,6 +3139,15 @@ async function checkAssertion(page, kind, value, timeoutMs = 3e3, assertion) {
|
|
|
3139
3139
|
}
|
|
3140
3140
|
fail(`expected ${sentence}`, last);
|
|
3141
3141
|
}
|
|
3142
|
+
function whereOn(url) {
|
|
3143
|
+
try {
|
|
3144
|
+
const u = new URL(url);
|
|
3145
|
+
if (u.protocol === "about:") return "";
|
|
3146
|
+
return `${u.pathname}${u.hash.startsWith("#/") ? u.hash.split("?")[0] : ""}`;
|
|
3147
|
+
} catch {
|
|
3148
|
+
return "";
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3142
3151
|
async function applyAction(loc, step, value, ctx = {}) {
|
|
3143
3152
|
switch (step.type) {
|
|
3144
3153
|
case "fill":
|
|
@@ -3620,6 +3629,10 @@ async function runStepsIn(session, steps, opts) {
|
|
|
3620
3629
|
result.foundBy = foundBy;
|
|
3621
3630
|
}
|
|
3622
3631
|
});
|
|
3632
|
+
try {
|
|
3633
|
+
result.where = whereOn(actionPage.url());
|
|
3634
|
+
} catch {
|
|
3635
|
+
}
|
|
3623
3636
|
for (const a of step.proposals ?? []) {
|
|
3624
3637
|
if (a.enabled === false) continue;
|
|
3625
3638
|
const label = describeAssertion(a, step.name);
|
|
@@ -10169,7 +10182,8 @@ var ASK_MODE_TOOLS = [
|
|
|
10169
10182
|
"knowledge_search",
|
|
10170
10183
|
"knowledge_read",
|
|
10171
10184
|
"read_test_cases",
|
|
10172
|
-
"list_my_branches"
|
|
10185
|
+
"list_my_branches",
|
|
10186
|
+
"run_insights"
|
|
10173
10187
|
];
|
|
10174
10188
|
function buildAskArgs(opts) {
|
|
10175
10189
|
const ask = opts.mode === "ask";
|
|
@@ -10177,7 +10191,7 @@ function buildAskArgs(opts) {
|
|
|
10177
10191
|
const brief = opts.brief?.trim();
|
|
10178
10192
|
const guidance = [
|
|
10179
10193
|
`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."),
|
|
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
|
|
10194
|
+
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 learned something about the app the next job should know (a screen, a rule, a quirk, what failed and why), write it with write_knowledge (memory/sessions/<date>-<topic>.md, append) \u2014 Flowriki keeps it and gives it to the next job.",
|
|
10181
10195
|
"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.",
|
|
10182
10196
|
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.` : "",
|
|
10183
10197
|
brief ? `
|
|
@@ -656,6 +656,8 @@ import { z as z7 } from "zod";
|
|
|
656
656
|
import { z as z8 } from "zod";
|
|
657
657
|
import { z as z9 } from "zod";
|
|
658
658
|
import { z as z10 } from "zod";
|
|
659
|
+
import { z as z11 } from "zod";
|
|
660
|
+
import { z as z12 } from "zod";
|
|
659
661
|
var EngineSchema = z3.enum(["chromium", "firefox", "webkit", "android", "ios"]);
|
|
660
662
|
var CapabilitySchema = z3.object({
|
|
661
663
|
id: z3.string(),
|
|
@@ -1820,6 +1822,42 @@ function decodeEntities(text) {
|
|
|
1820
1822
|
return out;
|
|
1821
1823
|
}
|
|
1822
1824
|
var nameKey = (name) => decodeEntities(name).toLowerCase().replace(/&/g, " and ").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
1825
|
+
var PLATFORM_PART = "(web|android|ios)";
|
|
1826
|
+
var LEARNED_PATH = new RegExp(`^(explore\\/${PLATFORM_PART}\\.md|memory\\/sessions\\/[a-z0-9][a-z0-9-]{0,120}\\.md)$`);
|
|
1827
|
+
var isLearnedPath = (path8) => LEARNED_PATH.test(path8);
|
|
1828
|
+
var LearnedSchema = z11.object({
|
|
1829
|
+
id: z11.string().regex(/^lrn_[a-z0-9_-]+$/),
|
|
1830
|
+
projectId: z11.string().min(1),
|
|
1831
|
+
/** explore: the app as the assistant walked it, one per platform.
|
|
1832
|
+
* session: what one job learned. */
|
|
1833
|
+
kind: z11.enum(["explore", "session"]),
|
|
1834
|
+
/** Its page path — what it is read and searched by. */
|
|
1835
|
+
path: z11.string().refine(isLearnedPath, "not a learned page"),
|
|
1836
|
+
platform: z11.enum(["web", "android", "ios"]).optional(),
|
|
1837
|
+
content: z11.string().max(4e5),
|
|
1838
|
+
by: z11.string().max(200).default(""),
|
|
1839
|
+
/** new until a person acts on it: promoted into a written page, or
|
|
1840
|
+
* dismissed — kept out of the assistant's brief and search. */
|
|
1841
|
+
status: z11.enum(["new", "promoted", "dismissed"]).default("new"),
|
|
1842
|
+
createdAt: z11.string(),
|
|
1843
|
+
updatedAt: z11.string()
|
|
1844
|
+
}).strict();
|
|
1845
|
+
var RUN_MEMORY_WEEKS = 5;
|
|
1846
|
+
var Bucket = z12.object({
|
|
1847
|
+
week: z12.number().int(),
|
|
1848
|
+
runs: z12.number().int().nonnegative(),
|
|
1849
|
+
steps: z12.record(z12.string(), z12.unknown()),
|
|
1850
|
+
screens: z12.record(z12.string(), z12.unknown()),
|
|
1851
|
+
links: z12.record(z12.string(), z12.unknown())
|
|
1852
|
+
}).strict();
|
|
1853
|
+
var RunMemorySchema = z12.object({
|
|
1854
|
+
id: z12.string().regex(/^rm_/),
|
|
1855
|
+
projectId: z12.string().min(1),
|
|
1856
|
+
/** Filled from the runs already kept, once — after that, run by run. */
|
|
1857
|
+
backfilled: z12.boolean().default(false),
|
|
1858
|
+
buckets: z12.array(Bucket).max(RUN_MEMORY_WEEKS + 1),
|
|
1859
|
+
updatedAt: z12.number()
|
|
1860
|
+
}).strict();
|
|
1823
1861
|
|
|
1824
1862
|
// ../engine-appium/dist/index.js
|
|
1825
1863
|
import path5 from "path";
|
|
@@ -3765,6 +3803,7 @@ async function performSignIn(session, step, opts) {
|
|
|
3765
3803
|
}
|
|
3766
3804
|
return { ok: true, label, detail: "" };
|
|
3767
3805
|
}
|
|
3806
|
+
var MOVES = /* @__PURE__ */ new Set(["click", "tap", "longPress", "press", "back", "navigate", "launchApp", "resetApp", "signIn", "signOut", "useHelper"]);
|
|
3768
3807
|
async function runMobileSteps(session, steps, opts) {
|
|
3769
3808
|
const results = [];
|
|
3770
3809
|
let status = "passed";
|
|
@@ -3830,6 +3869,10 @@ async function runMobileSteps(session, steps, opts) {
|
|
|
3830
3869
|
result.failureKind = e.kind ?? failureKind(e);
|
|
3831
3870
|
result.detail = firstLine(e);
|
|
3832
3871
|
}
|
|
3872
|
+
if (result.status === "passed" && MOVES.has(step.type)) {
|
|
3873
|
+
const at = await session.currentScreenName().catch(() => "");
|
|
3874
|
+
if (at) result.where = at;
|
|
3875
|
+
}
|
|
3833
3876
|
const shots = opts.screenshots ?? "on-failure";
|
|
3834
3877
|
if (opts.evidenceDir && (shots === "always" || shots === "on-failure" && result.status === "failed")) {
|
|
3835
3878
|
const file = `step-${String((opts.stepOffset ?? 0) + results.length + 1).padStart(2, "0")}.png`;
|
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
runtimeValue,
|
|
25
25
|
start,
|
|
26
26
|
status
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-GPSLXDGQ.js";
|
|
28
28
|
import {
|
|
29
29
|
CLOUD_PROVIDERS,
|
|
30
30
|
CLOUD_PROVIDER_NAME,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
slugify,
|
|
53
53
|
stepRefusedOn,
|
|
54
54
|
targetFor
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-YCFTNJQU.js";
|
|
56
56
|
|
|
57
57
|
// src/cli.ts
|
|
58
58
|
import { parseArgs } from "util";
|
|
@@ -266,6 +266,11 @@ var ServiceClient = class {
|
|
|
266
266
|
if (!ASK_ID) return;
|
|
267
267
|
await this.call(`/api/agent/jobs/draft?projectId=${encodeURIComponent(projectId)}`, { method: "POST", body: JSON.stringify({ draft }) });
|
|
268
268
|
}
|
|
269
|
+
/** What runs taught us: failing steps, and the app's map. */
|
|
270
|
+
insights(projectId, document) {
|
|
271
|
+
const q = new URLSearchParams({ projectId, ...document ? { document } : {} });
|
|
272
|
+
return this.call(`/api/agent/insights?${q}`);
|
|
273
|
+
}
|
|
269
274
|
/** The test case being built, as it stands, for the dashboard's job. */
|
|
270
275
|
async reportCase(projectId, live) {
|
|
271
276
|
if (!ASK_ID) return;
|
|
@@ -1780,6 +1785,33 @@ Helpers:${c.helpers.length ? "" : " none \u2014 create_helper makes one"}`,
|
|
|
1780
1785
|
].filter(Boolean).join("\n"));
|
|
1781
1786
|
}
|
|
1782
1787
|
},
|
|
1788
|
+
{
|
|
1789
|
+
name: "run_insights",
|
|
1790
|
+
title: "What runs taught us",
|
|
1791
|
+
description: "What the project's runs of the last 30 days show: the steps that fail most often \u2014 how often, whether they also pass sometimes (unreliable rather than broken), the last error \u2014 and the app's map: each page (web) or screen (Android) runs reached, what was done there, and which step leads where. Read it before healing a test, planning new ones, or answering what the project knows. document: one flow or test case, by name or id.",
|
|
1792
|
+
inputSchema: { type: "object", properties: { projectId: { type: "string" }, document: { type: "string" } }, additionalProperties: false },
|
|
1793
|
+
annotations: { readOnlyHint: true },
|
|
1794
|
+
run: async (args) => {
|
|
1795
|
+
const projectId = await projectFor(args["projectId"]);
|
|
1796
|
+
const r = await service.insights(projectId, str(args["document"]));
|
|
1797
|
+
if (!r.runs) return text("No runs in the last 30 days \u2014 nothing learned from runs yet.");
|
|
1798
|
+
const lines = [`From the last ${r.runs} run${r.runs === 1 ? "" : "s"} (30 days).`];
|
|
1799
|
+
lines.push(r.failing.length ? "\nSteps that failed, most often first:" : "\nNo step failed.");
|
|
1800
|
+
for (const f of r.failing.slice(0, 30)) {
|
|
1801
|
+
lines.push(`- ${f.document} (${f.documentKind}): ${f.step} \u2014 failed ${f.failures} of ${f.runs}${f.flaky ? ", passes sometimes (unreliable)" : ""}${f.lastKind ? `; last: ${f.lastKind}` : ""}${f.lastDetail ? ` \u2014 ${f.lastDetail}` : ""}`);
|
|
1802
|
+
}
|
|
1803
|
+
for (const m of r.maps) {
|
|
1804
|
+
lines.push(`
|
|
1805
|
+
${m.platform === "web" ? "Web pages" : "Android screens"} runs reached:`);
|
|
1806
|
+
for (const sc of m.screens.slice(0, 30)) lines.push(`- ${sc.where} (${sc.visits}\xD7)${sc.actions.length ? `: ${sc.actions.join(", ")}` : ""}`);
|
|
1807
|
+
if (m.links.length) {
|
|
1808
|
+
lines.push("Which leads where:");
|
|
1809
|
+
for (const l of m.links.slice(0, 40)) lines.push(`- ${l.from} \u2192 ${l.to} by ${l.via} (${l.count}\xD7)`);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
return text(lines.join("\n"));
|
|
1813
|
+
}
|
|
1814
|
+
},
|
|
1783
1815
|
{
|
|
1784
1816
|
name: "start_case",
|
|
1785
1817
|
title: "Start building a test case",
|
|
@@ -2458,7 +2490,7 @@ ${pages.filter((p) => !shown.has(p.path)).map((p) => `- ${p.path}`).join("\n") |
|
|
|
2458
2490
|
{
|
|
2459
2491
|
name: "write_knowledge",
|
|
2460
2492
|
title: "Write a knowledge page",
|
|
2461
|
-
description: `Write what the project should remember, on Claude's working branch for review: product.md (About the product \u2014 the person's own description; write it only when they ask) and product/<web|android|ios>.md (what is particular to that platform's app), explore/<web|android|ios>.md (what you saw walking that platform: "# Explored: <Platform>", "Explored: <yyyy-mm-dd>", "Environment: <name>", "Screens: <n>" lines, then Screens, Journeys, Rules observed, Vocabulary, Suggested features), codebase/<folder>.md (what a linked folder holds \u2014 starting with "# <folder>", "Commit: <sha>" and "Learned: <yyyy-mm-dd>" lines, then routes, screens, forms and their validation, roles, APIs, flags; summarised, never copied code), features/<name>.md (screens, rules, quirks), memory/decisions.md (decisions and conventions), memory/sessions/<yyyy-mm-dd>-<topic>.md (what a job did). append: true adds to the end instead of replacing. Never a password, key or token \u2014 those are refused.`,
|
|
2493
|
+
description: `Write what the project should remember, on Claude's working branch for review: product.md (About the product \u2014 the person's own description; write it only when they ask) and product/<web|android|ios>.md (what is particular to that platform's app), explore/<web|android|ios>.md (what you saw walking that platform: "# Explored: <Platform>", "Explored: <yyyy-mm-dd>", "Environment: <name>", "Screens: <n>" lines, then Screens, Journeys, Rules observed, Vocabulary, Suggested features), codebase/<folder>.md (what a linked folder holds \u2014 starting with "# <folder>", "Commit: <sha>" and "Learned: <yyyy-mm-dd>" lines, then routes, screens, forms and their validation, roles, APIs, flags; summarised, never copied code), features/<name>.md (screens, rules, quirks), memory/decisions.md (decisions and conventions), memory/sessions/<yyyy-mm-dd>-<topic>.md (what a job did \u2014 what you learned about the app that the next job should know). explore/\u2026 and memory/sessions/\u2026 are KEPT BY FLOWRIKI at once \u2014 no branch, nothing to publish \u2014 and given to the next job; the rest go on Claude's working branch for review. append: true adds to the end instead of replacing. Never a password, key or token \u2014 those are refused.`,
|
|
2462
2494
|
inputSchema: {
|
|
2463
2495
|
type: "object",
|
|
2464
2496
|
properties: { projectId: { type: "string" }, path: { type: "string" }, content: { type: "string" }, append: { type: "boolean" }, branch: { type: "string" } },
|
|
@@ -2473,7 +2505,7 @@ ${pages.filter((p) => !shown.has(p.path)).map((p) => `- ${p.path}`).join("\n") |
|
|
|
2473
2505
|
...args["append"] === true ? { append: true } : {},
|
|
2474
2506
|
...str(args["branch"]) ? { changeId: str(args["branch"]) } : {}
|
|
2475
2507
|
});
|
|
2476
|
-
return text(`Wrote ${saved.path} (${saved.length} characters) on Claude's working branch ${saved.branch}.`);
|
|
2508
|
+
return text(saved.keptBy === "flowriki" ? `Kept ${saved.path} (${saved.length} characters) in the project's memory \u2014 the next job is given it; nothing to publish.` : `Wrote ${saved.path} (${saved.length} characters) on Claude's working branch ${saved.branch}.`);
|
|
2477
2509
|
}
|
|
2478
2510
|
},
|
|
2479
2511
|
{
|
package/dist/engine.js
CHANGED
package/package.json
CHANGED