@iamu-dev/iamu 0.2.0-beta.5 → 0.2.0-beta.6
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/bin.js +153 -6
- package/dist/commands/start-run.d.ts +52 -0
- package/dist/index.js +153 -6
- package/dist/templates/skills/iamu-run.md +9 -5
- package/package.json +3 -3
package/dist/bin.js
CHANGED
|
@@ -200,17 +200,24 @@ function defaultConfig(params) {
|
|
|
200
200
|
cloud_api_url: null
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
|
-
var baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
203
|
+
var modelTierSchema, projectModelsConfigSchema, baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
204
204
|
var init_config = __esm({
|
|
205
205
|
"../../packages/protocol/dist/schemas/frontmatter/config.js"() {
|
|
206
206
|
"use strict";
|
|
207
207
|
init_common();
|
|
208
208
|
init_version();
|
|
209
|
+
modelTierSchema = z4.string().min(1).max(128).describe("Model role tier ('fast', 'thorough', 'inherit') or a specific model identifier.");
|
|
210
|
+
projectModelsConfigSchema = z4.object({
|
|
211
|
+
walker: modelTierSchema.optional(),
|
|
212
|
+
matcher: modelTierSchema.optional(),
|
|
213
|
+
judge: modelTierSchema.optional()
|
|
214
|
+
}).strict();
|
|
209
215
|
baseProjectFields = {
|
|
210
216
|
schema_version: z4.literal(SCHEMA_VERSION),
|
|
211
217
|
id: uuidSchema,
|
|
212
218
|
project_slug: slugSchema,
|
|
213
|
-
project_name: z4.string().min(1)
|
|
219
|
+
project_name: z4.string().min(1),
|
|
220
|
+
models: projectModelsConfigSchema.optional()
|
|
214
221
|
};
|
|
215
222
|
localOnlyConfig = z4.object({
|
|
216
223
|
...baseProjectFields,
|
|
@@ -1154,7 +1161,21 @@ var init_walkthrough_bundle = __esm({
|
|
|
1154
1161
|
judge_prompt: z19.string().optional(),
|
|
1155
1162
|
judge_model: z19.string().optional()
|
|
1156
1163
|
}).optional()
|
|
1157
|
-
}).optional()
|
|
1164
|
+
}).optional(),
|
|
1165
|
+
models: z19.object({
|
|
1166
|
+
walker: z19.string().optional(),
|
|
1167
|
+
matcher: z19.string().optional(),
|
|
1168
|
+
judge: z19.string().optional(),
|
|
1169
|
+
walker_reported: z19.string().optional(),
|
|
1170
|
+
matcher_reported: z19.string().optional(),
|
|
1171
|
+
judge_reported: z19.string().optional(),
|
|
1172
|
+
walker_tier: z19.string().optional(),
|
|
1173
|
+
matcher_tier: z19.string().optional(),
|
|
1174
|
+
judge_tier: z19.string().optional(),
|
|
1175
|
+
execution_mode: z19.enum(["multi_agent", "single_agent"]).optional(),
|
|
1176
|
+
velocity_sec_per_step: z19.number().optional(),
|
|
1177
|
+
total_duration_sec: z19.number().optional()
|
|
1178
|
+
}).optional().describe("AI model configuration and reported runtime provenance for this walkthrough.")
|
|
1158
1179
|
});
|
|
1159
1180
|
walkthroughBundleSchema = z19.object({
|
|
1160
1181
|
manifest: bundleManifestSchema,
|
|
@@ -1377,6 +1398,7 @@ var init_walkthrough_canonical = __esm({
|
|
|
1377
1398
|
build: buildMetadataSchema.optional(),
|
|
1378
1399
|
viewport: runViewportSchema.optional(),
|
|
1379
1400
|
environment: z20.string().optional(),
|
|
1401
|
+
models: z20.record(z20.unknown()).optional(),
|
|
1380
1402
|
findings: z20.array(canonicalFindingSchema),
|
|
1381
1403
|
steps: z20.array(canonicalStepSchema),
|
|
1382
1404
|
human_tests: z20.array(canonicalHumanTestSchema),
|
|
@@ -7248,6 +7270,7 @@ async function buildCanonical(projectRoot, manifest, completedAt, inputs) {
|
|
|
7248
7270
|
...manifest.build ? { build: manifest.build } : {},
|
|
7249
7271
|
...manifest.viewport ? { viewport: manifest.viewport } : {},
|
|
7250
7272
|
...manifest.environment ? { environment: manifest.environment } : {},
|
|
7273
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7251
7274
|
findings: canonicalFindings,
|
|
7252
7275
|
steps: canonicalSteps,
|
|
7253
7276
|
human_tests: humanTests,
|
|
@@ -7408,6 +7431,20 @@ function renderCanonicalMarkdown(canonical) {
|
|
|
7408
7431
|
lines.push(`- Viewport: ${canonical.viewport.device} (${canonical.viewport.width}\xD7${canonical.viewport.height})`);
|
|
7409
7432
|
}
|
|
7410
7433
|
lines.push(`- Started: ${canonical.provenance.started_at}`, `- Completed: ${canonical.provenance.completed_at}`, `- Walker prompt: ${canonical.provenance.walker_prompt.id}@${canonical.provenance.walker_prompt.version}`, `- Findings: ${canonical.findings.length}`, `- Steps: ${canonical.steps.length}`);
|
|
7434
|
+
const models = canonical.models;
|
|
7435
|
+
if (models) {
|
|
7436
|
+
const parts = [
|
|
7437
|
+
models.walker ? `Walker: ${models.walker}${models.walker_reported ? ` (${models.walker_reported})` : ""}` : "",
|
|
7438
|
+
models.judge ? `Judge: ${models.judge}${models.judge_reported ? ` (${models.judge_reported})` : ""}` : "",
|
|
7439
|
+
models.matcher ? `Matcher: ${models.matcher}` : ""
|
|
7440
|
+
].filter(Boolean);
|
|
7441
|
+
if (parts.length > 0) {
|
|
7442
|
+
lines.push(`- Models: ${parts.join(" \xB7 ")}`);
|
|
7443
|
+
}
|
|
7444
|
+
if (typeof models.velocity_sec_per_step === "number") {
|
|
7445
|
+
lines.push(`- Velocity: ~${models.velocity_sec_per_step}s/step (${models.total_duration_sec}s total)`);
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7411
7448
|
const what = canonical.what_worked;
|
|
7412
7449
|
if (what && what.length > 0) {
|
|
7413
7450
|
lines.push("", "## What worked");
|
|
@@ -7777,6 +7814,7 @@ async function assembleWalkthroughBundle(projectRoot, runId) {
|
|
|
7777
7814
|
report_markdown: report3,
|
|
7778
7815
|
verdict: verdictArray,
|
|
7779
7816
|
judge_meta: judgeMeta,
|
|
7817
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7780
7818
|
...buildFields(manifest),
|
|
7781
7819
|
...await readCanonicalExtras(projectRoot, runId)
|
|
7782
7820
|
},
|
|
@@ -14434,6 +14472,18 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14434
14472
|
}
|
|
14435
14473
|
const build = detectBuild({ cwd: projectRoot, label: opts.build });
|
|
14436
14474
|
const environment = resolveEnvironment(opts.env, start2.url);
|
|
14475
|
+
const cfg = await readConfig(projectRoot).catch(() => null);
|
|
14476
|
+
const walker = opts.walker ?? process.env.IAMU_WALKER_MODEL ?? cfg?.models?.walker ?? "fast";
|
|
14477
|
+
const judge = opts.judge ?? process.env.IAMU_JUDGE_MODEL ?? cfg?.models?.judge ?? "thorough";
|
|
14478
|
+
const matcher = opts.matcher ?? process.env.IAMU_MATCHER_MODEL ?? cfg?.models?.matcher ?? "thorough";
|
|
14479
|
+
const models = {
|
|
14480
|
+
walker,
|
|
14481
|
+
judge,
|
|
14482
|
+
matcher,
|
|
14483
|
+
walker_tier: ["fast", "thorough", "inherit"].includes(walker) ? walker : "custom",
|
|
14484
|
+
judge_tier: ["fast", "thorough", "inherit"].includes(judge) ? judge : "custom",
|
|
14485
|
+
matcher_tier: ["fast", "thorough", "inherit"].includes(matcher) ? matcher : "custom"
|
|
14486
|
+
};
|
|
14437
14487
|
let revisit = [];
|
|
14438
14488
|
try {
|
|
14439
14489
|
revisit = await computeRevisitPlaces(projectRoot, scenarioSlug, start2.url);
|
|
@@ -14451,6 +14501,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14451
14501
|
status: "running",
|
|
14452
14502
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14453
14503
|
completed_at: null,
|
|
14504
|
+
models,
|
|
14454
14505
|
...build ? { build } : {},
|
|
14455
14506
|
...environment ? { environment } : {},
|
|
14456
14507
|
...revisit.length > 0 ? { revisit } : {},
|
|
@@ -14472,6 +14523,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14472
14523
|
...targetWarning ? { target_warning: targetWarning } : {},
|
|
14473
14524
|
persona_slug: fm.persona,
|
|
14474
14525
|
outline_slug: fm.outline,
|
|
14526
|
+
models,
|
|
14475
14527
|
build: build ?? null,
|
|
14476
14528
|
environment: environment ?? null,
|
|
14477
14529
|
revisit,
|
|
@@ -14490,6 +14542,9 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14490
14542
|
console.log(` ${chalk11.dim("persona:")} ${fm.persona}`);
|
|
14491
14543
|
console.log(` ${chalk11.dim("outline:")} ${fm.outline}`);
|
|
14492
14544
|
console.log(` ${chalk11.dim("target:")} ${start2.url}`);
|
|
14545
|
+
console.log(
|
|
14546
|
+
` ${chalk11.dim("models:")} walker: ${chalk11.cyan(walker)} \xB7 judge: ${chalk11.cyan(judge)} \xB7 matcher: ${chalk11.cyan(matcher)}`
|
|
14547
|
+
);
|
|
14493
14548
|
if (targetWarning) {
|
|
14494
14549
|
console.log(` ${symbols.warning} ${chalk11.yellow(targetWarning)}`);
|
|
14495
14550
|
}
|
|
@@ -16673,6 +16728,7 @@ __export(browse_exports, {
|
|
|
16673
16728
|
readBrowseState: () => readBrowseState,
|
|
16674
16729
|
shouldDisableSandbox: () => shouldDisableSandbox,
|
|
16675
16730
|
start: () => start,
|
|
16731
|
+
waitForVisualStability: () => waitForVisualStability,
|
|
16676
16732
|
withPage: () => withPage
|
|
16677
16733
|
});
|
|
16678
16734
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
@@ -16879,6 +16935,45 @@ async function stop(opts, driver) {
|
|
|
16879
16935
|
}
|
|
16880
16936
|
report2(opts, { stopped: true }, [` ${chalk20.green("\u2713")} Browser stopped`]);
|
|
16881
16937
|
}
|
|
16938
|
+
async function waitForVisualStability(page, maxWaitMs = 300) {
|
|
16939
|
+
let timer;
|
|
16940
|
+
try {
|
|
16941
|
+
const nodeTimeout = new Promise((resolve4) => {
|
|
16942
|
+
timer = setTimeout(resolve4, maxWaitMs + 100);
|
|
16943
|
+
});
|
|
16944
|
+
const browserWait = page.evaluate((maxWait) => {
|
|
16945
|
+
return new Promise((resolve4) => {
|
|
16946
|
+
const timeout = setTimeout(resolve4, maxWait);
|
|
16947
|
+
const done = () => {
|
|
16948
|
+
clearTimeout(timeout);
|
|
16949
|
+
resolve4();
|
|
16950
|
+
};
|
|
16951
|
+
const g = globalThis;
|
|
16952
|
+
if (typeof g.requestAnimationFrame !== "function") {
|
|
16953
|
+
done();
|
|
16954
|
+
return;
|
|
16955
|
+
}
|
|
16956
|
+
g.requestAnimationFrame(() => {
|
|
16957
|
+
g.requestAnimationFrame?.(() => {
|
|
16958
|
+
const animations = g.document?.getAnimations ? g.document.getAnimations().filter((a) => a.playState === "running" && a.finished) : [];
|
|
16959
|
+
if (animations.length === 0) {
|
|
16960
|
+
done();
|
|
16961
|
+
return;
|
|
16962
|
+
}
|
|
16963
|
+
Promise.allSettled(
|
|
16964
|
+
animations.map((a) => a.finished)
|
|
16965
|
+
).finally(done);
|
|
16966
|
+
});
|
|
16967
|
+
});
|
|
16968
|
+
});
|
|
16969
|
+
}, maxWaitMs).catch(() => {
|
|
16970
|
+
});
|
|
16971
|
+
await Promise.race([browserWait, nodeTimeout]);
|
|
16972
|
+
} catch {
|
|
16973
|
+
} finally {
|
|
16974
|
+
if (timer) clearTimeout(timer);
|
|
16975
|
+
}
|
|
16976
|
+
}
|
|
16882
16977
|
async function withPage(opts, driver, fn, target) {
|
|
16883
16978
|
const state = readBrowseState();
|
|
16884
16979
|
if (!state) {
|
|
@@ -16898,9 +16993,9 @@ async function withPage(opts, driver, fn, target) {
|
|
|
16898
16993
|
await fn(page);
|
|
16899
16994
|
await page.waitForLoadState("domcontentloaded").catch(() => {
|
|
16900
16995
|
});
|
|
16901
|
-
await page.waitForTimeout(400);
|
|
16902
16996
|
let screenshot = null;
|
|
16903
16997
|
if (opts.shot) {
|
|
16998
|
+
await waitForVisualStability(page);
|
|
16904
16999
|
screenshot = path22.resolve(opts.shot);
|
|
16905
17000
|
mkdirSync(path22.dirname(screenshot), { recursive: true });
|
|
16906
17001
|
await page.screenshot({ path: screenshot, fullPage: Boolean(opts.full) });
|
|
@@ -18887,7 +18982,7 @@ function composeWalkerPrompt(p) {
|
|
|
18887
18982
|
`Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
|
|
18888
18983
|
`Record findings with: ${iamu} new-finding --scenario ${p.scenarioSlug} --run ${p.runId} --step <N> --title "..." --type <helps|hinders|missing|irrelevant|incorrect> --severity <critical|major|minor|observation> --ui-fact "..." --consequence "..." --task-impact <blocks|impairs|slows|neutral|accelerates|enables> --confidence <high|medium|low> --json \u2014 then add the body (description + "## Recommendation") below the frontmatter of the file it returns.`,
|
|
18889
18984
|
"`--step <N>` is the step whose screenshot shows the problem (or strength): it is the evidence people see next to the finding, so record that step and its screenshot first, and pick the screenshot where the thing is actually visible.",
|
|
18890
|
-
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...]} \u2014 one sentence each, in the persona's language.`,
|
|
18985
|
+
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...], "model": "<your model identifier if known, e.g. claude-3-5-haiku, gemini-3.8-flash, or unspecified>"} \u2014 one sentence each, in the persona's language.`,
|
|
18891
18986
|
"",
|
|
18892
18987
|
"Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
|
|
18893
18988
|
"",
|
|
@@ -19709,6 +19804,36 @@ async function complete(runId, opts) {
|
|
|
19709
19804
|
return;
|
|
19710
19805
|
}
|
|
19711
19806
|
const completedAt = pickCompletedAt(run.manifest);
|
|
19807
|
+
const walkerSummary = await readWalkerSummary(run.runDirPath);
|
|
19808
|
+
let judgeReported;
|
|
19809
|
+
try {
|
|
19810
|
+
const verdictsRaw = await fs26.readFile(
|
|
19811
|
+
path26.join(run.runDirPath, "verdicts.json"),
|
|
19812
|
+
"utf8"
|
|
19813
|
+
);
|
|
19814
|
+
const parsed = JSON.parse(verdictsRaw);
|
|
19815
|
+
if (parsed.provenance?.model) {
|
|
19816
|
+
judgeReported = parsed.provenance.model;
|
|
19817
|
+
}
|
|
19818
|
+
} catch {
|
|
19819
|
+
}
|
|
19820
|
+
const stepCount = provenanceAudit.step_count;
|
|
19821
|
+
const durationSec = provenanceAudit.duration_seconds ?? (run.manifest.created_at && Date.parse(completedAt) > Date.parse(run.manifest.created_at) ? Math.round(
|
|
19822
|
+
(Date.parse(completedAt) - Date.parse(run.manifest.created_at)) / 1e3 * 10
|
|
19823
|
+
) / 10 : null);
|
|
19824
|
+
const velocitySec = provenanceAudit.avg_step_interval_seconds ?? (durationSec != null && stepCount > 1 ? Math.round(durationSec / (stepCount - 1) * 10) / 10 : null);
|
|
19825
|
+
const existingModels = run.manifest.models ?? {};
|
|
19826
|
+
const updatedModels = {
|
|
19827
|
+
...existingModels,
|
|
19828
|
+
...walkerSummary.model ? { walker_reported: walkerSummary.model } : {},
|
|
19829
|
+
...judgeReported ? { judge_reported: judgeReported } : {},
|
|
19830
|
+
...durationSec != null ? { total_duration_sec: durationSec } : {},
|
|
19831
|
+
...velocitySec != null ? { velocity_sec_per_step: velocitySec } : {}
|
|
19832
|
+
};
|
|
19833
|
+
run.manifest = {
|
|
19834
|
+
...run.manifest,
|
|
19835
|
+
models: updatedModels
|
|
19836
|
+
};
|
|
19712
19837
|
const assembled = await assembleRun(
|
|
19713
19838
|
run.projectRoot,
|
|
19714
19839
|
runId,
|
|
@@ -19869,6 +19994,17 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
|
|
|
19869
19994
|
console.log(` steps ${count(canonical.steps.length)}`);
|
|
19870
19995
|
console.log(` started ${chalk30.dim(manifest.created_at)}`);
|
|
19871
19996
|
console.log(` completed ${chalk30.dim(completedAt)}`);
|
|
19997
|
+
const models = manifest.models;
|
|
19998
|
+
if (models?.velocity_sec_per_step != null) {
|
|
19999
|
+
console.log(
|
|
20000
|
+
` velocity ${chalk30.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk30.dim(`(${models.total_duration_sec}s total)`)}`
|
|
20001
|
+
);
|
|
20002
|
+
}
|
|
20003
|
+
if (models && (models.walker || models.judge || models.matcher)) {
|
|
20004
|
+
const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
|
|
20005
|
+
const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
|
|
20006
|
+
console.log(` models ${chalk30.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
|
|
20007
|
+
}
|
|
19872
20008
|
console.log();
|
|
19873
20009
|
console.log(
|
|
19874
20010
|
` ${chalk30.dim("canonical:")} ${chalk30.dim(path26.relative(projectRoot, assembled.canonicalPath))}`
|
|
@@ -19962,9 +20098,11 @@ async function readWalkerSummary(runDirPath) {
|
|
|
19962
20098
|
await fs26.readFile(path26.join(runDirPath, "summary.json"), "utf8")
|
|
19963
20099
|
);
|
|
19964
20100
|
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
|
|
20101
|
+
const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
|
|
19965
20102
|
return {
|
|
19966
20103
|
what_worked: strings(data.what_worked),
|
|
19967
|
-
open_questions: strings(data.open_questions)
|
|
20104
|
+
open_questions: strings(data.open_questions),
|
|
20105
|
+
model
|
|
19968
20106
|
};
|
|
19969
20107
|
} catch {
|
|
19970
20108
|
return {};
|
|
@@ -20754,6 +20892,15 @@ function registerWalkthroughCommands(program2) {
|
|
|
20754
20892
|
).option(
|
|
20755
20893
|
"--env <name>",
|
|
20756
20894
|
"Environment the product runs in (default: the target URL's origin)"
|
|
20895
|
+
).option(
|
|
20896
|
+
"--walker <tierOrModel>",
|
|
20897
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for driving browser navigation (default: fast)"
|
|
20898
|
+
).option(
|
|
20899
|
+
"--judge <tierOrModel>",
|
|
20900
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for human test evaluation (default: thorough)"
|
|
20901
|
+
).option(
|
|
20902
|
+
"--matcher <tierOrModel>",
|
|
20903
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for finding matching (default: thorough)"
|
|
20757
20904
|
).option(
|
|
20758
20905
|
"--check",
|
|
20759
20906
|
"Enforce target URL reachability (fail fast if server is offline)"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type BuildMetadata, type RunViewport, type ScenarioFrontmatter } from "@iamu-dev/protocol";
|
|
2
|
+
/**
|
|
3
|
+
* The URL a run starts at: `--target` (or the scenario's `target_url`)
|
|
4
|
+
* as the base, with the scenario's `entry_path` resolved against it.
|
|
5
|
+
* Returns an error message instead when there is nothing usable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveStartUrl(target: string | undefined, scenario: Pick<ScenarioFrontmatter, "target_url" | "entry_path">): {
|
|
8
|
+
url: string;
|
|
9
|
+
} | {
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Fast preflight probe to check if the target URL answers HTTP requests.
|
|
14
|
+
* Times out quickly (default: 1000ms) to avoid stalling walkthrough initialization.
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkTargetReachable(url: string, timeoutMs?: number): Promise<{
|
|
17
|
+
reachable: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* The build as one line: label, branch, commit and CI provider. A dirty
|
|
22
|
+
* tree shows next to the commit, so a re-run after an uncommitted fix
|
|
23
|
+
* visibly walks a different build.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* formatBuild({ git_sha: "90e2444…", git_dirty: true, worktree_hash: "3fa9c1d2e4b5" });
|
|
28
|
+
* // "90e2444 + uncommitted changes (3fa9c1…)"
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatBuild(build: BuildMetadata): string;
|
|
32
|
+
/**
|
|
33
|
+
* A run viewport as one line.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* formatViewport({ device: "desktop", width: 1280, height: 800, source: "persona-tags" });
|
|
38
|
+
* // "desktop 1280×800 (from persona tags)"
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatViewport(v: RunViewport): string;
|
|
42
|
+
export interface StartRunOptions {
|
|
43
|
+
target?: string;
|
|
44
|
+
build?: string;
|
|
45
|
+
env?: string;
|
|
46
|
+
json?: boolean;
|
|
47
|
+
check?: boolean;
|
|
48
|
+
walker?: string;
|
|
49
|
+
judge?: string;
|
|
50
|
+
matcher?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function startRun(scenarioSlug: string, opts: StartRunOptions): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -199,17 +199,24 @@ function defaultConfig(params) {
|
|
|
199
199
|
cloud_api_url: null
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
-
var baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
202
|
+
var modelTierSchema, projectModelsConfigSchema, baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
203
203
|
var init_config = __esm({
|
|
204
204
|
"../../packages/protocol/dist/schemas/frontmatter/config.js"() {
|
|
205
205
|
"use strict";
|
|
206
206
|
init_common();
|
|
207
207
|
init_version();
|
|
208
|
+
modelTierSchema = z4.string().min(1).max(128).describe("Model role tier ('fast', 'thorough', 'inherit') or a specific model identifier.");
|
|
209
|
+
projectModelsConfigSchema = z4.object({
|
|
210
|
+
walker: modelTierSchema.optional(),
|
|
211
|
+
matcher: modelTierSchema.optional(),
|
|
212
|
+
judge: modelTierSchema.optional()
|
|
213
|
+
}).strict();
|
|
208
214
|
baseProjectFields = {
|
|
209
215
|
schema_version: z4.literal(SCHEMA_VERSION),
|
|
210
216
|
id: uuidSchema,
|
|
211
217
|
project_slug: slugSchema,
|
|
212
|
-
project_name: z4.string().min(1)
|
|
218
|
+
project_name: z4.string().min(1),
|
|
219
|
+
models: projectModelsConfigSchema.optional()
|
|
213
220
|
};
|
|
214
221
|
localOnlyConfig = z4.object({
|
|
215
222
|
...baseProjectFields,
|
|
@@ -1153,7 +1160,21 @@ var init_walkthrough_bundle = __esm({
|
|
|
1153
1160
|
judge_prompt: z19.string().optional(),
|
|
1154
1161
|
judge_model: z19.string().optional()
|
|
1155
1162
|
}).optional()
|
|
1156
|
-
}).optional()
|
|
1163
|
+
}).optional(),
|
|
1164
|
+
models: z19.object({
|
|
1165
|
+
walker: z19.string().optional(),
|
|
1166
|
+
matcher: z19.string().optional(),
|
|
1167
|
+
judge: z19.string().optional(),
|
|
1168
|
+
walker_reported: z19.string().optional(),
|
|
1169
|
+
matcher_reported: z19.string().optional(),
|
|
1170
|
+
judge_reported: z19.string().optional(),
|
|
1171
|
+
walker_tier: z19.string().optional(),
|
|
1172
|
+
matcher_tier: z19.string().optional(),
|
|
1173
|
+
judge_tier: z19.string().optional(),
|
|
1174
|
+
execution_mode: z19.enum(["multi_agent", "single_agent"]).optional(),
|
|
1175
|
+
velocity_sec_per_step: z19.number().optional(),
|
|
1176
|
+
total_duration_sec: z19.number().optional()
|
|
1177
|
+
}).optional().describe("AI model configuration and reported runtime provenance for this walkthrough.")
|
|
1157
1178
|
});
|
|
1158
1179
|
walkthroughBundleSchema = z19.object({
|
|
1159
1180
|
manifest: bundleManifestSchema,
|
|
@@ -1376,6 +1397,7 @@ var init_walkthrough_canonical = __esm({
|
|
|
1376
1397
|
build: buildMetadataSchema.optional(),
|
|
1377
1398
|
viewport: runViewportSchema.optional(),
|
|
1378
1399
|
environment: z20.string().optional(),
|
|
1400
|
+
models: z20.record(z20.unknown()).optional(),
|
|
1379
1401
|
findings: z20.array(canonicalFindingSchema),
|
|
1380
1402
|
steps: z20.array(canonicalStepSchema),
|
|
1381
1403
|
human_tests: z20.array(canonicalHumanTestSchema),
|
|
@@ -7247,6 +7269,7 @@ async function buildCanonical(projectRoot, manifest, completedAt, inputs) {
|
|
|
7247
7269
|
...manifest.build ? { build: manifest.build } : {},
|
|
7248
7270
|
...manifest.viewport ? { viewport: manifest.viewport } : {},
|
|
7249
7271
|
...manifest.environment ? { environment: manifest.environment } : {},
|
|
7272
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7250
7273
|
findings: canonicalFindings,
|
|
7251
7274
|
steps: canonicalSteps,
|
|
7252
7275
|
human_tests: humanTests,
|
|
@@ -7407,6 +7430,20 @@ function renderCanonicalMarkdown(canonical) {
|
|
|
7407
7430
|
lines.push(`- Viewport: ${canonical.viewport.device} (${canonical.viewport.width}\xD7${canonical.viewport.height})`);
|
|
7408
7431
|
}
|
|
7409
7432
|
lines.push(`- Started: ${canonical.provenance.started_at}`, `- Completed: ${canonical.provenance.completed_at}`, `- Walker prompt: ${canonical.provenance.walker_prompt.id}@${canonical.provenance.walker_prompt.version}`, `- Findings: ${canonical.findings.length}`, `- Steps: ${canonical.steps.length}`);
|
|
7433
|
+
const models = canonical.models;
|
|
7434
|
+
if (models) {
|
|
7435
|
+
const parts = [
|
|
7436
|
+
models.walker ? `Walker: ${models.walker}${models.walker_reported ? ` (${models.walker_reported})` : ""}` : "",
|
|
7437
|
+
models.judge ? `Judge: ${models.judge}${models.judge_reported ? ` (${models.judge_reported})` : ""}` : "",
|
|
7438
|
+
models.matcher ? `Matcher: ${models.matcher}` : ""
|
|
7439
|
+
].filter(Boolean);
|
|
7440
|
+
if (parts.length > 0) {
|
|
7441
|
+
lines.push(`- Models: ${parts.join(" \xB7 ")}`);
|
|
7442
|
+
}
|
|
7443
|
+
if (typeof models.velocity_sec_per_step === "number") {
|
|
7444
|
+
lines.push(`- Velocity: ~${models.velocity_sec_per_step}s/step (${models.total_duration_sec}s total)`);
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7410
7447
|
const what = canonical.what_worked;
|
|
7411
7448
|
if (what && what.length > 0) {
|
|
7412
7449
|
lines.push("", "## What worked");
|
|
@@ -7776,6 +7813,7 @@ async function assembleWalkthroughBundle(projectRoot, runId) {
|
|
|
7776
7813
|
report_markdown: report3,
|
|
7777
7814
|
verdict: verdictArray,
|
|
7778
7815
|
judge_meta: judgeMeta,
|
|
7816
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7779
7817
|
...buildFields(manifest),
|
|
7780
7818
|
...await readCanonicalExtras(projectRoot, runId)
|
|
7781
7819
|
},
|
|
@@ -14413,6 +14451,18 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14413
14451
|
}
|
|
14414
14452
|
const build = detectBuild({ cwd: projectRoot, label: opts.build });
|
|
14415
14453
|
const environment = resolveEnvironment(opts.env, start2.url);
|
|
14454
|
+
const cfg = await readConfig(projectRoot).catch(() => null);
|
|
14455
|
+
const walker = opts.walker ?? process.env.IAMU_WALKER_MODEL ?? cfg?.models?.walker ?? "fast";
|
|
14456
|
+
const judge = opts.judge ?? process.env.IAMU_JUDGE_MODEL ?? cfg?.models?.judge ?? "thorough";
|
|
14457
|
+
const matcher = opts.matcher ?? process.env.IAMU_MATCHER_MODEL ?? cfg?.models?.matcher ?? "thorough";
|
|
14458
|
+
const models = {
|
|
14459
|
+
walker,
|
|
14460
|
+
judge,
|
|
14461
|
+
matcher,
|
|
14462
|
+
walker_tier: ["fast", "thorough", "inherit"].includes(walker) ? walker : "custom",
|
|
14463
|
+
judge_tier: ["fast", "thorough", "inherit"].includes(judge) ? judge : "custom",
|
|
14464
|
+
matcher_tier: ["fast", "thorough", "inherit"].includes(matcher) ? matcher : "custom"
|
|
14465
|
+
};
|
|
14416
14466
|
let revisit = [];
|
|
14417
14467
|
try {
|
|
14418
14468
|
revisit = await computeRevisitPlaces(projectRoot, scenarioSlug, start2.url);
|
|
@@ -14430,6 +14480,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14430
14480
|
status: "running",
|
|
14431
14481
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14432
14482
|
completed_at: null,
|
|
14483
|
+
models,
|
|
14433
14484
|
...build ? { build } : {},
|
|
14434
14485
|
...environment ? { environment } : {},
|
|
14435
14486
|
...revisit.length > 0 ? { revisit } : {},
|
|
@@ -14451,6 +14502,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14451
14502
|
...targetWarning ? { target_warning: targetWarning } : {},
|
|
14452
14503
|
persona_slug: fm.persona,
|
|
14453
14504
|
outline_slug: fm.outline,
|
|
14505
|
+
models,
|
|
14454
14506
|
build: build ?? null,
|
|
14455
14507
|
environment: environment ?? null,
|
|
14456
14508
|
revisit,
|
|
@@ -14469,6 +14521,9 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14469
14521
|
console.log(` ${chalk11.dim("persona:")} ${fm.persona}`);
|
|
14470
14522
|
console.log(` ${chalk11.dim("outline:")} ${fm.outline}`);
|
|
14471
14523
|
console.log(` ${chalk11.dim("target:")} ${start2.url}`);
|
|
14524
|
+
console.log(
|
|
14525
|
+
` ${chalk11.dim("models:")} walker: ${chalk11.cyan(walker)} \xB7 judge: ${chalk11.cyan(judge)} \xB7 matcher: ${chalk11.cyan(matcher)}`
|
|
14526
|
+
);
|
|
14472
14527
|
if (targetWarning) {
|
|
14473
14528
|
console.log(` ${symbols.warning} ${chalk11.yellow(targetWarning)}`);
|
|
14474
14529
|
}
|
|
@@ -16652,6 +16707,7 @@ __export(browse_exports, {
|
|
|
16652
16707
|
readBrowseState: () => readBrowseState,
|
|
16653
16708
|
shouldDisableSandbox: () => shouldDisableSandbox,
|
|
16654
16709
|
start: () => start,
|
|
16710
|
+
waitForVisualStability: () => waitForVisualStability,
|
|
16655
16711
|
withPage: () => withPage
|
|
16656
16712
|
});
|
|
16657
16713
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
@@ -16858,6 +16914,45 @@ async function stop(opts, driver) {
|
|
|
16858
16914
|
}
|
|
16859
16915
|
report2(opts, { stopped: true }, [` ${chalk20.green("\u2713")} Browser stopped`]);
|
|
16860
16916
|
}
|
|
16917
|
+
async function waitForVisualStability(page, maxWaitMs = 300) {
|
|
16918
|
+
let timer;
|
|
16919
|
+
try {
|
|
16920
|
+
const nodeTimeout = new Promise((resolve3) => {
|
|
16921
|
+
timer = setTimeout(resolve3, maxWaitMs + 100);
|
|
16922
|
+
});
|
|
16923
|
+
const browserWait = page.evaluate((maxWait) => {
|
|
16924
|
+
return new Promise((resolve3) => {
|
|
16925
|
+
const timeout = setTimeout(resolve3, maxWait);
|
|
16926
|
+
const done = () => {
|
|
16927
|
+
clearTimeout(timeout);
|
|
16928
|
+
resolve3();
|
|
16929
|
+
};
|
|
16930
|
+
const g = globalThis;
|
|
16931
|
+
if (typeof g.requestAnimationFrame !== "function") {
|
|
16932
|
+
done();
|
|
16933
|
+
return;
|
|
16934
|
+
}
|
|
16935
|
+
g.requestAnimationFrame(() => {
|
|
16936
|
+
g.requestAnimationFrame?.(() => {
|
|
16937
|
+
const animations = g.document?.getAnimations ? g.document.getAnimations().filter((a) => a.playState === "running" && a.finished) : [];
|
|
16938
|
+
if (animations.length === 0) {
|
|
16939
|
+
done();
|
|
16940
|
+
return;
|
|
16941
|
+
}
|
|
16942
|
+
Promise.allSettled(
|
|
16943
|
+
animations.map((a) => a.finished)
|
|
16944
|
+
).finally(done);
|
|
16945
|
+
});
|
|
16946
|
+
});
|
|
16947
|
+
});
|
|
16948
|
+
}, maxWaitMs).catch(() => {
|
|
16949
|
+
});
|
|
16950
|
+
await Promise.race([browserWait, nodeTimeout]);
|
|
16951
|
+
} catch {
|
|
16952
|
+
} finally {
|
|
16953
|
+
if (timer) clearTimeout(timer);
|
|
16954
|
+
}
|
|
16955
|
+
}
|
|
16861
16956
|
async function withPage(opts, driver, fn, target) {
|
|
16862
16957
|
const state = readBrowseState();
|
|
16863
16958
|
if (!state) {
|
|
@@ -16877,9 +16972,9 @@ async function withPage(opts, driver, fn, target) {
|
|
|
16877
16972
|
await fn(page);
|
|
16878
16973
|
await page.waitForLoadState("domcontentloaded").catch(() => {
|
|
16879
16974
|
});
|
|
16880
|
-
await page.waitForTimeout(400);
|
|
16881
16975
|
let screenshot = null;
|
|
16882
16976
|
if (opts.shot) {
|
|
16977
|
+
await waitForVisualStability(page);
|
|
16883
16978
|
screenshot = path22.resolve(opts.shot);
|
|
16884
16979
|
mkdirSync(path22.dirname(screenshot), { recursive: true });
|
|
16885
16980
|
await page.screenshot({ path: screenshot, fullPage: Boolean(opts.full) });
|
|
@@ -18866,7 +18961,7 @@ function composeWalkerPrompt(p) {
|
|
|
18866
18961
|
`Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
|
|
18867
18962
|
`Record findings with: ${iamu} new-finding --scenario ${p.scenarioSlug} --run ${p.runId} --step <N> --title "..." --type <helps|hinders|missing|irrelevant|incorrect> --severity <critical|major|minor|observation> --ui-fact "..." --consequence "..." --task-impact <blocks|impairs|slows|neutral|accelerates|enables> --confidence <high|medium|low> --json \u2014 then add the body (description + "## Recommendation") below the frontmatter of the file it returns.`,
|
|
18868
18963
|
"`--step <N>` is the step whose screenshot shows the problem (or strength): it is the evidence people see next to the finding, so record that step and its screenshot first, and pick the screenshot where the thing is actually visible.",
|
|
18869
|
-
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...]} \u2014 one sentence each, in the persona's language.`,
|
|
18964
|
+
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...], "model": "<your model identifier if known, e.g. claude-3-5-haiku, gemini-3.8-flash, or unspecified>"} \u2014 one sentence each, in the persona's language.`,
|
|
18870
18965
|
"",
|
|
18871
18966
|
"Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
|
|
18872
18967
|
"",
|
|
@@ -19688,6 +19783,36 @@ async function complete(runId, opts) {
|
|
|
19688
19783
|
return;
|
|
19689
19784
|
}
|
|
19690
19785
|
const completedAt = pickCompletedAt(run.manifest);
|
|
19786
|
+
const walkerSummary = await readWalkerSummary(run.runDirPath);
|
|
19787
|
+
let judgeReported;
|
|
19788
|
+
try {
|
|
19789
|
+
const verdictsRaw = await fs26.readFile(
|
|
19790
|
+
path26.join(run.runDirPath, "verdicts.json"),
|
|
19791
|
+
"utf8"
|
|
19792
|
+
);
|
|
19793
|
+
const parsed = JSON.parse(verdictsRaw);
|
|
19794
|
+
if (parsed.provenance?.model) {
|
|
19795
|
+
judgeReported = parsed.provenance.model;
|
|
19796
|
+
}
|
|
19797
|
+
} catch {
|
|
19798
|
+
}
|
|
19799
|
+
const stepCount = provenanceAudit.step_count;
|
|
19800
|
+
const durationSec = provenanceAudit.duration_seconds ?? (run.manifest.created_at && Date.parse(completedAt) > Date.parse(run.manifest.created_at) ? Math.round(
|
|
19801
|
+
(Date.parse(completedAt) - Date.parse(run.manifest.created_at)) / 1e3 * 10
|
|
19802
|
+
) / 10 : null);
|
|
19803
|
+
const velocitySec = provenanceAudit.avg_step_interval_seconds ?? (durationSec != null && stepCount > 1 ? Math.round(durationSec / (stepCount - 1) * 10) / 10 : null);
|
|
19804
|
+
const existingModels = run.manifest.models ?? {};
|
|
19805
|
+
const updatedModels = {
|
|
19806
|
+
...existingModels,
|
|
19807
|
+
...walkerSummary.model ? { walker_reported: walkerSummary.model } : {},
|
|
19808
|
+
...judgeReported ? { judge_reported: judgeReported } : {},
|
|
19809
|
+
...durationSec != null ? { total_duration_sec: durationSec } : {},
|
|
19810
|
+
...velocitySec != null ? { velocity_sec_per_step: velocitySec } : {}
|
|
19811
|
+
};
|
|
19812
|
+
run.manifest = {
|
|
19813
|
+
...run.manifest,
|
|
19814
|
+
models: updatedModels
|
|
19815
|
+
};
|
|
19691
19816
|
const assembled = await assembleRun(
|
|
19692
19817
|
run.projectRoot,
|
|
19693
19818
|
runId,
|
|
@@ -19848,6 +19973,17 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
|
|
|
19848
19973
|
console.log(` steps ${count(canonical.steps.length)}`);
|
|
19849
19974
|
console.log(` started ${chalk30.dim(manifest.created_at)}`);
|
|
19850
19975
|
console.log(` completed ${chalk30.dim(completedAt)}`);
|
|
19976
|
+
const models = manifest.models;
|
|
19977
|
+
if (models?.velocity_sec_per_step != null) {
|
|
19978
|
+
console.log(
|
|
19979
|
+
` velocity ${chalk30.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk30.dim(`(${models.total_duration_sec}s total)`)}`
|
|
19980
|
+
);
|
|
19981
|
+
}
|
|
19982
|
+
if (models && (models.walker || models.judge || models.matcher)) {
|
|
19983
|
+
const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
|
|
19984
|
+
const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
|
|
19985
|
+
console.log(` models ${chalk30.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
|
|
19986
|
+
}
|
|
19851
19987
|
console.log();
|
|
19852
19988
|
console.log(
|
|
19853
19989
|
` ${chalk30.dim("canonical:")} ${chalk30.dim(path26.relative(projectRoot, assembled.canonicalPath))}`
|
|
@@ -19941,9 +20077,11 @@ async function readWalkerSummary(runDirPath) {
|
|
|
19941
20077
|
await fs26.readFile(path26.join(runDirPath, "summary.json"), "utf8")
|
|
19942
20078
|
);
|
|
19943
20079
|
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
|
|
20080
|
+
const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
|
|
19944
20081
|
return {
|
|
19945
20082
|
what_worked: strings(data.what_worked),
|
|
19946
|
-
open_questions: strings(data.open_questions)
|
|
20083
|
+
open_questions: strings(data.open_questions),
|
|
20084
|
+
model
|
|
19947
20085
|
};
|
|
19948
20086
|
} catch {
|
|
19949
20087
|
return {};
|
|
@@ -20725,6 +20863,15 @@ function registerWalkthroughCommands(program) {
|
|
|
20725
20863
|
).option(
|
|
20726
20864
|
"--env <name>",
|
|
20727
20865
|
"Environment the product runs in (default: the target URL's origin)"
|
|
20866
|
+
).option(
|
|
20867
|
+
"--walker <tierOrModel>",
|
|
20868
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for driving browser navigation (default: fast)"
|
|
20869
|
+
).option(
|
|
20870
|
+
"--judge <tierOrModel>",
|
|
20871
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for human test evaluation (default: thorough)"
|
|
20872
|
+
).option(
|
|
20873
|
+
"--matcher <tierOrModel>",
|
|
20874
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for finding matching (default: thorough)"
|
|
20728
20875
|
).option(
|
|
20729
20876
|
"--check",
|
|
20730
20877
|
"Enforce target URL reachability (fail fast if server is offline)"
|
|
@@ -185,12 +185,16 @@ The result's `walker_prompt` is the walker's entire prompt. Pass it verbatim —
|
|
|
185
185
|
|
|
186
186
|
A fresh subagent is NOT blind by default. On most hosts it still loads the project's agent instructions and can read files in the repo, so it may know the domain model, the route table, and why a screen was built the way it was — none of which the persona knows. The methodology's "Stay outside the product" rule is what keeps it honest; it is part of `walker_prompt` — never remove or soften it. If the host lets you restrict a subagent's tools, give the walker only the driving surface, file write/edit, and a shell for `iamu` commands.
|
|
187
187
|
|
|
188
|
+
**Model tiering & subagent dispatch:**
|
|
189
|
+
- **Walker (Browser Navigation):** High turn count, mechanical tool-use. Use the **fast** model tier (`flash` or `haiku`) by default to cut walk time by ~60% and conserve session quota.
|
|
190
|
+
- **Matcher & Judge:** Broad synthesis and semantic comparison. Use the **thorough** frontier tier (`pro`, `sonnet`, or active model) to prevent severity inflation.
|
|
191
|
+
|
|
188
192
|
```
|
|
189
193
|
# In Claude Code:
|
|
190
|
-
Agent({ description: "Walk <scenario-slug> as <persona.name>", prompt: "<walker_prompt, verbatim>" })
|
|
194
|
+
Agent({ description: "Walk <scenario-slug> as <persona.name>", model: "haiku", prompt: "<walker_prompt, verbatim>" })
|
|
191
195
|
|
|
192
196
|
# In Antigravity / generic subagent hosts:
|
|
193
|
-
invoke_subagent(Role="Persona Walker", Prompt="<walker_prompt, verbatim>")
|
|
197
|
+
invoke_subagent(Role="Persona Walker", Model="flash", Prompt="<walker_prompt, verbatim>")
|
|
194
198
|
```
|
|
195
199
|
|
|
196
200
|
The subagent inherits the driving-surface tools and drives the product independently. It does not see this conversation.
|
|
@@ -202,7 +206,7 @@ The subagent inherits the driving-surface tools and drives the product independe
|
|
|
202
206
|
- **Screenshot every 3-4 taps at most.** Batching more desyncs the moment an answer opens a screen she did not expect.
|
|
203
207
|
- **Coordinates are device points, origin top-left.** A scaled screenshot must be mapped back to that space before tapping.
|
|
204
208
|
|
|
205
|
-
**If subagent dispatch is not supported by your host editor** (e.g. standard Cursor
|
|
209
|
+
**If subagent dispatch or model-switching is not supported by your host editor** (e.g. standard Cursor / Windsurf single-session mode), fall back to walking directly in the current session (inheriting the active model) — but strictly adhere to the persona behavioral constraints in `walker_prompt`.
|
|
206
210
|
|
|
207
211
|
After the walker returns, continue to the post-walk pipeline below.
|
|
208
212
|
|
|
@@ -269,7 +273,7 @@ This reads all steps, findings, and entity files, assembles `canonical.json`, ge
|
|
|
269
273
|
iamu match <run-id> --json
|
|
270
274
|
```
|
|
271
275
|
|
|
272
|
-
If `needed` is `false` (no earlier findings for this scenario, or none this run), skip to C. Otherwise, read the prompt from `iamu/.runs/<run-id>/match_prompt.md` directly and dispatch a matcher subagent with it as its only context. Write its JSON answer to `<matches.json>` and record it:
|
|
276
|
+
If `needed` is `false` (no earlier findings for this scenario, or none this run), skip to C. Otherwise, read the prompt from `iamu/.runs/<run-id>/match_prompt.md` directly and dispatch a matcher subagent with it as its only context (use the **thorough** model tier, e.g. `Model="pro"` in Antigravity, to accurately evaluate semantic equivalence). Write its JSON answer to `<matches.json>` and record it:
|
|
273
277
|
|
|
274
278
|
```bash
|
|
275
279
|
iamu match-record <run-id> --file <matches.json>
|
|
@@ -283,7 +287,7 @@ This links recurrences to their earlier canonical finding, marks earlier finding
|
|
|
283
287
|
iamu evaluate <run-id> --json
|
|
284
288
|
```
|
|
285
289
|
|
|
286
|
-
Read the evaluation prompt from `iamu/.runs/<run-id>/judge_prompt.md` directly. Dispatch the evaluator as a subagent with that prompt. After the evaluator returns verdicts, record them locally with `--no-push`:
|
|
290
|
+
Read the evaluation prompt from `iamu/.runs/<run-id>/judge_prompt.md` directly. Dispatch the evaluator as a subagent with that prompt (use the **thorough** model tier, e.g. `Model="pro"` in Antigravity, to prevent severity inflation). After the evaluator returns verdicts, record them locally with `--no-push`:
|
|
287
291
|
|
|
288
292
|
```bash
|
|
289
293
|
iamu evaluate-record <run-id> --file <verdicts.json> --no-prompt --no-push
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iamu-dev/iamu",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Test your product as realistic personas — scenario-driven development CLI",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
"tsup": "^8.5.1",
|
|
58
58
|
"typescript": "^5.8.0",
|
|
59
59
|
"vitest": "^4.1.11",
|
|
60
|
+
"@iamu-dev/protocol": "0.1.0",
|
|
60
61
|
"@iamu-dev/local-store": "0.1.0",
|
|
61
|
-
"@iamu/tsconfig": "0.1.0"
|
|
62
|
-
"@iamu-dev/protocol": "0.1.0"
|
|
62
|
+
"@iamu/tsconfig": "0.1.0"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "tsup && tsc -b tsconfig.build.json --emitDeclarationOnly && node scripts/copy-assets.mjs",
|