@asrunjs/asrun 0.12.0 → 0.14.0
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/.claude/skills/exploring-cases/SKILL.md +26 -11
- package/.claude/skills/exploring-cases/references/handoff-card.js +81 -7
- package/.claude/skills/exploring-cases/references/open-questions.md +2 -2
- package/.claude/skills/exploring-cases/references/readiness.md +6 -0
- package/.claude/skills/exploring-cases/references/user-handoff.md +60 -27
- package/AGENTS.md +3 -2
- package/README.md +1 -1
- package/dist/lib/desktop-notify.d.ts +38 -0
- package/dist/lib/desktop-notify.js +158 -0
- package/dist/lib/paths.d.ts +9 -2
- package/dist/lib/paths.js +32 -5
- package/dist/lib/pdf.js +23 -3
- package/dist/scripts/check-cases.js +3 -3
- package/dist/scripts/cli.js +9 -2
- package/dist/scripts/doctor.js +39 -1
- package/dist/scripts/notify-test.d.ts +1 -0
- package/dist/scripts/notify-test.js +48 -0
- package/dist/ui/server/actions.js +4 -2
- package/dist/ui/server/ai-stream.d.ts +9 -3
- package/dist/ui/server/ai-stream.js +216 -37
- package/dist/ui/server/jobs.d.ts +10 -0
- package/dist/ui/server/jobs.js +41 -1
- package/dist/ui/shared/ai-commands.js +4 -2
- package/dist/ui/shared/log-text.d.ts +13 -0
- package/dist/ui/shared/log-text.js +45 -0
- package/dist/ui/shared/types.d.ts +8 -1
- package/docs/agents/case-format.md +2 -1
- package/docs/ai-workflow.md +11 -7
- package/docs/cases.md +5 -5
- package/docs/commands.md +20 -1
- package/docs/install.md +1 -1
- package/docs/troubleshooting.md +17 -0
- package/docs/web-ui.md +25 -8
- package/package.json +4 -2
- package/templates/.claude/commands/explore-case.md +3 -2
- package/templates/package.json +1 -1
- package/ui/dist/assets/{dist-CJnqQMvX.js → dist-BlFnpAOk.js} +1 -1
- package/ui/dist/assets/{dist-D8TUm3Ai.js → dist-BwXTr_k4.js} +1 -1
- package/ui/dist/assets/{dist-Clj0wGFy.js → dist-BzDmxZ9V.js} +1 -1
- package/ui/dist/assets/{dist-BjlbPf3t.js → dist-C1uAVsF-.js} +1 -1
- package/ui/dist/assets/{dist-CaAcCNRS.js → dist-CjFzi6xl.js} +1 -1
- package/ui/dist/assets/{dist-DkAx5a-Y.js → dist-D7YBl9nc.js} +1 -1
- package/ui/dist/assets/{dist-Bfv586Or.js → dist-DxeJFPaO.js} +1 -1
- package/ui/dist/assets/{dist-DUDqh_3e.js → dist-DxlXH2E-.js} +1 -1
- package/ui/dist/assets/{dist-BX88bMYN.js → dist-fpvlcqX7.js} +1 -1
- package/ui/dist/assets/{dist-cuboTVh-.js → dist-jGvx4PBr.js} +1 -1
- package/ui/dist/assets/index-Bo4XKJvP.css +2 -0
- package/ui/dist/assets/index-Djsj43vv.js +376 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DaUQORCy.js +0 -353
- package/ui/dist/assets/index-j3W4BBCj.css +0 -2
package/dist/lib/paths.d.ts
CHANGED
|
@@ -30,7 +30,14 @@ export declare const EXPLORE_DIR: string;
|
|
|
30
30
|
export declare const TEMPLATES_DIR: string;
|
|
31
31
|
export declare const SKILLS_DIR: string;
|
|
32
32
|
/**
|
|
33
|
-
* The
|
|
34
|
-
* install). Tools that need a package folder rather than a module (vite's config, pdfjs assets) use
|
|
33
|
+
* The folder of a package the framework needs at run time: the framework's own dependency, or the workspace's (a
|
|
34
|
+
* hoisted or linked install). Tools that need a package folder rather than a module (vite's config, pdfjs assets) use
|
|
35
|
+
* this.
|
|
36
|
+
*
|
|
37
|
+
* Node's own resolution comes first, because it is the same one every `import` in this package goes through: the
|
|
38
|
+
* folder and the module that is loaded can then never disagree. A hand-built path could not promise that — under
|
|
39
|
+
* pnpm's virtual store the framework sits in `<store>/node_modules/@asrunjs/asrun`, so the folder that holds its
|
|
40
|
+
* dependencies is one `node_modules`, not two, and appending a second one named the wrong folder and made every PDF
|
|
41
|
+
* check fail with "pdfjs-dist is not installed" while the package was installed.
|
|
35
42
|
*/
|
|
36
43
|
export declare function packageDir(packageName: string): string;
|
package/dist/lib/paths.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
/**
|
|
4
5
|
* Where the framework's own code and assets live: the package folder (`node_modules/@asrunjs/asrun`). The compiled files sit in
|
|
5
6
|
* `dist/lib`, so one level up from there is the package root again.
|
|
@@ -87,13 +88,39 @@ export const EXPLORE_DIR = path.join(ROOT, '.explore');
|
|
|
87
88
|
export const TEMPLATES_DIR = path.join(FRAMEWORK_ROOT, 'templates');
|
|
88
89
|
export const SKILLS_DIR = path.join(FRAMEWORK_ROOT, '.claude', 'skills');
|
|
89
90
|
/**
|
|
90
|
-
* The
|
|
91
|
-
* install). Tools that need a package folder rather than a module (vite's config, pdfjs assets) use
|
|
91
|
+
* The folder of a package the framework needs at run time: the framework's own dependency, or the workspace's (a
|
|
92
|
+
* hoisted or linked install). Tools that need a package folder rather than a module (vite's config, pdfjs assets) use
|
|
93
|
+
* this.
|
|
94
|
+
*
|
|
95
|
+
* Node's own resolution comes first, because it is the same one every `import` in this package goes through: the
|
|
96
|
+
* folder and the module that is loaded can then never disagree. A hand-built path could not promise that — under
|
|
97
|
+
* pnpm's virtual store the framework sits in `<store>/node_modules/@asrunjs/asrun`, so the folder that holds its
|
|
98
|
+
* dependencies is one `node_modules`, not two, and appending a second one named the wrong folder and made every PDF
|
|
99
|
+
* check fail with "pdfjs-dist is not installed" while the package was installed.
|
|
92
100
|
*/
|
|
93
101
|
export function packageDir(packageName) {
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
const dir = path.
|
|
102
|
+
// A bare name resolves to the package's entry point only; asking for the manifest is the portable way to its folder.
|
|
103
|
+
try {
|
|
104
|
+
const dir = path.dirname(createRequire(import.meta.url).resolve(`${packageName}/package.json`));
|
|
105
|
+
if (fs.existsSync(path.join(dir, 'package.json')))
|
|
106
|
+
return dir;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Not resolvable from here (an `exports` map without "./package.json", or a package installed elsewhere): the
|
|
110
|
+
// folders that can hold an install answer instead.
|
|
111
|
+
}
|
|
112
|
+
// pnpm's virtual store already keeps a package's dependencies inside a `node_modules`, next to its scope folder;
|
|
113
|
+
// every other layout nests that folder inside the install root.
|
|
114
|
+
const store = path.dirname(path.dirname(FRAMEWORK_ROOT));
|
|
115
|
+
const candidates = [
|
|
116
|
+
// This package's own nested dependencies (an install strategy that nests instead of hoisting).
|
|
117
|
+
path.join(FRAMEWORK_ROOT, 'node_modules', packageName),
|
|
118
|
+
// The dependencies next to it: pnpm's store, or the root of a hoisted install.
|
|
119
|
+
path.basename(store) === 'node_modules' ? path.join(store, packageName) : path.join(store, 'node_modules', packageName),
|
|
120
|
+
// The workspace's own install: a peer dependency (`@playwright/test`) lives here.
|
|
121
|
+
path.join(ROOT, 'node_modules', packageName),
|
|
122
|
+
];
|
|
123
|
+
for (const dir of candidates) {
|
|
97
124
|
if (fs.existsSync(dir))
|
|
98
125
|
return dir;
|
|
99
126
|
}
|
package/dist/lib/pdf.js
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { createCanvas } from '@napi-rs/canvas';
|
|
4
5
|
import { expect } from '@playwright/test';
|
|
5
6
|
import { packageDir } from './paths.js';
|
|
6
7
|
let pdfjs;
|
|
7
|
-
const loadPdfjs = () => (pdfjs ??= import('pdfjs-dist/legacy/build/pdf.mjs'))
|
|
8
|
-
//
|
|
8
|
+
const loadPdfjs = () => (pdfjs ??= import('pdfjs-dist/legacy/build/pdf.mjs').catch((error) => {
|
|
9
|
+
// The one dependency of this module. A raw ERR_MODULE_NOT_FOUND names this file and nothing a person can act on.
|
|
10
|
+
throw new Error(`The PDF renderer is unavailable: "pdfjs-dist" could not be loaded (${error.message}). The asrun install is ` +
|
|
11
|
+
'incomplete: run "pnpm install" in the workspace root, or install the framework again ("pnpm add -D ' +
|
|
12
|
+
'@asrunjs/asrun"). "pnpm asrun doctor" says which part is missing.');
|
|
13
|
+
}));
|
|
14
|
+
// pdf.js loads its fonts, cmaps and wasm files from the package folder, not from the consuming project. The folder is
|
|
15
|
+
// derived from the very module that is imported, so it cannot drift from the code that reads it: a hand-built path was
|
|
16
|
+
// one `node_modules` too deep under pnpm's virtual store, and every PDF check failed although the package was there.
|
|
17
|
+
// `packageDir` answers only when the module itself cannot be resolved (then the load above reports it).
|
|
9
18
|
let pdfjsDir;
|
|
10
|
-
|
|
19
|
+
function pdfjsRoot() {
|
|
20
|
+
if (pdfjsDir !== undefined)
|
|
21
|
+
return pdfjsDir;
|
|
22
|
+
try {
|
|
23
|
+
const build = createRequire(import.meta.url).resolve('pdfjs-dist/legacy/build/pdf.mjs');
|
|
24
|
+
return (pdfjsDir = path.resolve(path.dirname(build), '..', '..'));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return (pdfjsDir = packageDir('pdfjs-dist'));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const dirUrl = (dir) => pathToFileURL(path.join(pdfjsRoot(), dir)).href + '/';
|
|
11
31
|
/** A corner of the page. Defaults: 30% of the width and 12% of the height, enough for a logo and nothing else. */
|
|
12
32
|
export function corner(name, size = {}) {
|
|
13
33
|
const width = size.width ?? 0.3;
|
|
@@ -58,7 +58,7 @@ function questionProblems(id) {
|
|
|
58
58
|
warnings.push(`the "✍️ Answer:" line of the question on line ${question.line} is not the last line of its block`);
|
|
59
59
|
}
|
|
60
60
|
if (!question.parent) {
|
|
61
|
-
warnings.push(`the question on line ${question.line} has no
|
|
61
|
+
warnings.push(`the question on line ${question.line} has no sentence it is about: accepting it would delete a claim nobody sees`);
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
const step = doc.steps.find((candidate) => candidate.line === question.parent?.line);
|
|
@@ -67,8 +67,8 @@ function questionProblems(id) {
|
|
|
67
67
|
}
|
|
68
68
|
const anchor = immediateAnchor(splitLines(doc.text), question.line, question.indent);
|
|
69
69
|
if (anchor !== null && anchor !== question.parent.line) {
|
|
70
|
-
const
|
|
71
|
-
warnings.push(`the question on line ${question.line} hangs under "${
|
|
70
|
+
const anchorText = splitLines(doc.text).find((line) => line.number === anchor)?.text.trim() ?? '';
|
|
71
|
+
warnings.push(`the question on line ${question.line} hangs under "${anchorText.slice(0, 80)}", but the panel shows "${question.parent.text.slice(0, 80)}" as the sentence it is about; indent it under that sentence or lift that sentence to a top-level bullet`);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
for (const step of doc.steps) {
|
package/dist/scripts/cli.js
CHANGED
|
@@ -44,13 +44,18 @@ async function main() {
|
|
|
44
44
|
await import('./explore-notes.js');
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
+
if (command === 'notify-test') {
|
|
48
|
+
const { main: notifyTest } = await import('./notify-test.js');
|
|
49
|
+
process.exitCode = await notifyTest(rest);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
47
52
|
if (command === 'help' || command === '--help-init') {
|
|
48
53
|
console.log(USAGE);
|
|
49
54
|
return;
|
|
50
55
|
}
|
|
51
56
|
await import('./run.js');
|
|
52
57
|
}
|
|
53
|
-
const USAGE = `Usage: pnpm asrun [init|doctor|rules|reset|upgrade|migrate|explore:notes] [options]
|
|
58
|
+
const USAGE = `Usage: pnpm asrun [init|doctor|rules|reset|upgrade|migrate|explore:notes|notify-test] [options]
|
|
54
59
|
|
|
55
60
|
(no subcommand) Run the tests of one environment. See "pnpm asrun --help".
|
|
56
61
|
init Create the files a workspace needs (never overwrites; --update refreshes generated files,
|
|
@@ -65,5 +70,7 @@ const USAGE = `Usage: pnpm asrun [init|doctor|rules|reset|upgrade|migrate|explor
|
|
|
65
70
|
target release for breaking changes (see "pnpm asrun upgrade --help").
|
|
66
71
|
migrate Apply the workspace migrations of the installed release, or list them (see "pnpm asrun migrate
|
|
67
72
|
--help"). "upgrade" runs it; use it after a manual install.
|
|
68
|
-
explore:notes Print or validate the local exploration note of a case (see "pnpm asrun explore:notes --help")
|
|
73
|
+
explore:notes Print or validate the local exploration note of a case (see "pnpm asrun explore:notes --help").
|
|
74
|
+
notify-test Show one desktop notification and print the command behind it, to tell a hidden notification from a
|
|
75
|
+
broken one (see "pnpm asrun notify-test --help").`;
|
|
69
76
|
await main();
|
package/dist/scripts/doctor.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { loadProfiles, listEnvs, PROFILES_FILE } from '../lib/profiles.js';
|
|
5
|
-
import { FRAMEWORK_ROOT, ROOT, SKILLS_DIR } from '../lib/paths.js';
|
|
5
|
+
import { FRAMEWORK_ROOT, ROOT, SKILLS_DIR, packageDir } from '../lib/paths.js';
|
|
6
6
|
import { readWorkspaceConfig } from '../lib/config.js';
|
|
7
7
|
import { readProjectRules, mentionedTestDataPrefixes } from '../lib/rules.js';
|
|
8
8
|
import { spawnTarget } from '../lib/command.js';
|
|
@@ -85,6 +85,33 @@ function documentedBrowserTools() {
|
|
|
85
85
|
const section = /^## Runnable\n([\s\S]*?)^## /m.exec(text)?.[1] ?? '';
|
|
86
86
|
return [...new Set(section.match(/browser_[a-z0-9_]+/g) ?? [])];
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The PDF renderer, which is how an exploring agent looks at a downloaded PDF and how a spec checks one: `lib/pdf.js`
|
|
90
|
+
* imports `pdfjs-dist` and `@napi-rs/canvas` at run time, and pdf.js reads its fonts and cmaps from the package folder
|
|
91
|
+
* `packageDir` names. Both are runtime dependencies of the framework, so a failure means the install (or the Node
|
|
92
|
+
* version) is broken — and it used to surface only when someone rendered a PDF, halfway through an exploration.
|
|
93
|
+
*/
|
|
94
|
+
async function pdfRenderer() {
|
|
95
|
+
let dir;
|
|
96
|
+
try {
|
|
97
|
+
dir = packageDir('pdfjs-dist');
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// `packageDir` already says where it looked; the caller adds what to do about it.
|
|
101
|
+
return { ok: false, reason: 'pdfjs-dist is not installed where the framework can reach it' };
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const version = JSON.parse(read(path.join(dir, 'package.json')) ?? '{}').version ?? 'unknown';
|
|
105
|
+
// Loading both is what proves it: the module import resolves exactly like the one in `lib/pdf.js`, and the native
|
|
106
|
+
// canvas is what draws the page.
|
|
107
|
+
await import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
108
|
+
await import('@napi-rs/canvas');
|
|
109
|
+
return { ok: true, version };
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return { ok: false, reason: error.message };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
88
115
|
export async function main(args) {
|
|
89
116
|
if (args.includes('--help')) {
|
|
90
117
|
console.log('Usage: pnpm asrun doctor');
|
|
@@ -296,6 +323,17 @@ export async function main(args) {
|
|
|
296
323
|
warnings.push(`${extra.pkg} is not installed and this machine does not provide it: ${extra.provides} is skipped. Run ` +
|
|
297
324
|
'"pnpm asrun init" (or "pnpm go") to install it, or install the tool yourself and run doctor again.');
|
|
298
325
|
}
|
|
326
|
+
// The PDF renderer: "pnpm pdf:render", the exploring agent's look at a downloaded PDF, and every `expectPdf` check.
|
|
327
|
+
const pdf = await pdfRenderer();
|
|
328
|
+
if (pdf.ok) {
|
|
329
|
+
facts.push(`pdf renderer: pdfjs-dist ${pdf.version} (canvas ok)`);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
// An Error message may already end with a period; the sentence around it must not double it.
|
|
333
|
+
const reason = pdf.reason.replace(/\.\s*$/, '');
|
|
334
|
+
errors.push(`The PDF renderer does not work, so "pnpm pdf:render" and every PDF check fail (${reason}). To fix it, run ` +
|
|
335
|
+
'"pnpm install" in the workspace root (Node 22.13 or newer is required), or install the framework again.');
|
|
336
|
+
}
|
|
299
337
|
let scripts = null;
|
|
300
338
|
try {
|
|
301
339
|
const packageJson = read(path.join(ROOT, 'package.json'));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describeCommand, notificationCommand, sendNotification } from '../lib/desktop-notify.js';
|
|
2
|
+
/*
|
|
3
|
+
* `pnpm asrun notify-test`
|
|
4
|
+
*
|
|
5
|
+
* Shows one desktop notification through the same module the UI server notifies with, and prints what it ran. The
|
|
6
|
+
* point is to separate "the code is wrong" from "this machine hides notifications": a notification that never appears
|
|
7
|
+
* while this command exits 0 means the operating system dropped it, and the hint below says where to look.
|
|
8
|
+
*/
|
|
9
|
+
const USAGE = `Usage: pnpm asrun notify-test [--help]
|
|
10
|
+
|
|
11
|
+
Shows one desktop notification and prints the command that showed it. Nothing is written to a case or a report.
|
|
12
|
+
|
|
13
|
+
Use it when "your turn" produced no notification: the UI server sends those through the same command.
|
|
14
|
+
macOS: System Settings > Notifications > the app that runs this (Terminal, iTerm2, ...).
|
|
15
|
+
Windows: Settings > System > Notifications, and turn off Focus assist while you test.
|
|
16
|
+
Linux: install libnotify (the "notify-send" command).`;
|
|
17
|
+
const HINT = {
|
|
18
|
+
darwin: 'If nothing appeared, the system dropped it: open System Settings > Notifications, find the app that runs this ' +
|
|
19
|
+
'command (Terminal, iTerm2, the editor you run pnpm in) and allow notifications for it. Focus mode hides them too.',
|
|
20
|
+
win32: 'If nothing appeared, check Settings > System > Notifications (and Focus assist), and that a notification banner is ' +
|
|
21
|
+
'not turned off for Windows PowerShell.',
|
|
22
|
+
linux: 'If nothing appeared, install libnotify (the "notify-send" command) and check that a notification daemon runs in ' +
|
|
23
|
+
'this session.',
|
|
24
|
+
};
|
|
25
|
+
export async function main(args) {
|
|
26
|
+
if (args.includes('--help')) {
|
|
27
|
+
console.log(USAGE);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
const notification = {
|
|
31
|
+
title: 'AsRun — test',
|
|
32
|
+
body: 'A desktop notification: this is how "your turn in the browser window" reaches you.',
|
|
33
|
+
sound: true,
|
|
34
|
+
};
|
|
35
|
+
const command = notificationCommand(process.platform, notification);
|
|
36
|
+
if (command)
|
|
37
|
+
console.log(`Running: ${describeCommand(command)}`);
|
|
38
|
+
const outcome = await sendNotification(notification);
|
|
39
|
+
if (outcome.ok) {
|
|
40
|
+
console.log('Done: a notification should be on screen now. If it is not, the system dropped it, not asrun.');
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
console.error(`No notification: ${outcome.reason ?? 'the command failed'}`);
|
|
44
|
+
const hint = HINT[process.platform];
|
|
45
|
+
if (hint)
|
|
46
|
+
console.error(hint);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
@@ -259,8 +259,10 @@ export function buildJobSpec(request) {
|
|
|
259
259
|
thinking: request.thinking,
|
|
260
260
|
force: request.force,
|
|
261
261
|
attended: request.attended,
|
|
262
|
-
//
|
|
263
|
-
|
|
262
|
+
// Always the picked ids, even when they name every profile of the environment: without the flag explore falls back
|
|
263
|
+
// to one logged-in profile per domain, which is not the same as "the profiles you ticked". Heal runs only the
|
|
264
|
+
// profiles that are logged in anyway.
|
|
265
|
+
profiles: request.profiles,
|
|
264
266
|
hints: hintsPath,
|
|
265
267
|
report: request.report,
|
|
266
268
|
};
|
|
@@ -13,6 +13,8 @@ export interface LogPart {
|
|
|
13
13
|
/** The agent has put a handoff card in the browser window and waits for the person (see user-handoff.md). */
|
|
14
14
|
export interface AwaitingUser {
|
|
15
15
|
message: string | null;
|
|
16
|
+
/** The finish condition the card shows in its "DONE WHEN" block; null when the options did not name one. */
|
|
17
|
+
finish: string | null;
|
|
16
18
|
since: number;
|
|
17
19
|
}
|
|
18
20
|
export declare class AiStreamFormatter {
|
|
@@ -21,8 +23,12 @@ export declare class AiStreamFormatter {
|
|
|
21
23
|
private pending;
|
|
22
24
|
private cardCalls;
|
|
23
25
|
private waitingForUser;
|
|
24
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The message and finish condition of the last options call, kept so a card that runs after unrelated calls still
|
|
28
|
+
* carries them.
|
|
29
|
+
*/
|
|
25
30
|
private lastHandoffMessage;
|
|
31
|
+
private lastHandoffFinish;
|
|
26
32
|
private lastEventAt;
|
|
27
33
|
private lastWaitAt;
|
|
28
34
|
/** The last call the agent finished, so a quiet spell can say what it is doing instead of only that nothing came. */
|
|
@@ -53,8 +59,8 @@ export declare class AiStreamFormatter {
|
|
|
53
59
|
/** A call the agent has started and not finished yet (Claude reports both ends). */
|
|
54
60
|
private start;
|
|
55
61
|
/**
|
|
56
|
-
* The end of a call, plus
|
|
57
|
-
*
|
|
62
|
+
* The end of a call, plus what it returned. `fallback` is for a CLI that reports a call only when it is over
|
|
63
|
+
* (OpenCode), which still knows when it started.
|
|
58
64
|
*/
|
|
59
65
|
private finishCall;
|
|
60
66
|
/** Said once per run, after the first tool call that Claude Code's sandbox refused (nobody is there to approve it). */
|