toolroll 0.5.0 → 0.7.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/README.md +38 -24
- package/dist/accent-colors.d.ts +6 -3
- package/dist/accent-colors.js +9 -4
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +23 -23
- package/dist/browser-workspace.d.ts +46 -0
- package/dist/builder.d.ts +18 -1
- package/dist/builder.js +177 -5
- package/dist/chat-delivery.js +6 -4
- package/dist/claim.d.ts +15 -1
- package/dist/claim.js +30 -2
- package/dist/control-ui.js +1 -1
- package/dist/daemon.d.ts +9 -0
- package/dist/daemon.js +21 -0
- package/dist/decision.d.ts +6 -0
- package/dist/decision.js +12 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/dispatch.d.ts +3 -1
- package/dist/dispatch.js +13 -0
- package/dist/dispose.js +11 -7
- package/dist/exhaustion.d.ts +14 -0
- package/dist/exhaustion.js +35 -0
- package/dist/install-method.d.ts +17 -0
- package/dist/install-method.js +61 -0
- package/dist/invoke.js +26 -0
- package/dist/lead-status.d.ts +7 -0
- package/dist/lead-status.js +15 -2
- package/dist/mate.js +5 -2
- package/dist/operate.d.ts +5 -0
- package/dist/operate.js +109 -15
- package/dist/provider-auth.d.ts +138 -0
- package/dist/provider-auth.js +291 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +8 -1
- package/dist/releases.d.ts +76 -0
- package/dist/releases.js +225 -0
- package/dist/serve.d.ts +4 -0
- package/dist/serve.js +207 -76
- package/dist/setup-guide.d.ts +2 -2
- package/dist/setup-guide.js +2 -0
- package/dist/skills.d.ts +6 -0
- package/dist/skills.js +8 -0
- package/dist/store.d.ts +2 -2
- package/dist/store.js +13 -4
- package/dist/subscription-chat.js +3 -0
- package/dist/update-notice.d.ts +5 -0
- package/dist/update-notice.js +4 -0
- package/dist/work-index.js +22 -4
- package/dist/worktree.d.ts +13 -0
- package/dist/worktree.js +55 -29
- package/package.json +1 -1
package/dist/dispose.js
CHANGED
|
@@ -28,6 +28,7 @@ const TICK_FAILURE_REASONS = new Set([
|
|
|
28
28
|
"agent",
|
|
29
29
|
"agent-reported",
|
|
30
30
|
"no-op",
|
|
31
|
+
"no-handoff",
|
|
31
32
|
"moved-head",
|
|
32
33
|
"moved-branch",
|
|
33
34
|
"timeout",
|
|
@@ -49,6 +50,7 @@ const STANDALONE_BROKE_REASONS = new Set([
|
|
|
49
50
|
"agent",
|
|
50
51
|
"agent-reported",
|
|
51
52
|
"no-op",
|
|
53
|
+
"no-handoff",
|
|
52
54
|
"moved-head",
|
|
53
55
|
"timeout",
|
|
54
56
|
"git",
|
|
@@ -339,13 +341,15 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
339
341
|
// classification trusts only what the machine itself observed.
|
|
340
342
|
const failureClass = result.reason === "agent-reported"
|
|
341
343
|
? "agent-reported"
|
|
342
|
-
: result.reason === "no-
|
|
343
|
-
? "no-
|
|
344
|
-
: result.reason === "
|
|
345
|
-
? "
|
|
346
|
-
: result.reason === "
|
|
347
|
-
? "
|
|
348
|
-
: "
|
|
344
|
+
: result.reason === "no-handoff"
|
|
345
|
+
? "no-handoff"
|
|
346
|
+
: result.reason === "no-op" || result.reason === "moved-head" || result.reason === "moved-branch"
|
|
347
|
+
? "no-op"
|
|
348
|
+
: result.reason === "timeout" || result.reason === "git" || result.reason === "provider-init" || result.reason === "setup" || result.reason === "stopped" || result.reason === "provider-unattested" || result.reason === "provider-protocol"
|
|
349
|
+
? "retryable-infra"
|
|
350
|
+
: result.reason === "commit-failure"
|
|
351
|
+
? "commit-failure"
|
|
352
|
+
: "unknown";
|
|
349
353
|
// The attempt's ending, in words, on the run itself: the class is what
|
|
350
354
|
// the strikes count, the message is what a person reads (an agent's
|
|
351
355
|
// own verdict was already recorded by the builder under the same key).
|
package/dist/exhaustion.d.ts
CHANGED
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
* class. Exit code, bare rate-limit prose, timeout, notFound, stderr,
|
|
21
21
|
* and zero tokens NEVER independently establish exhaustion.
|
|
22
22
|
* - Everything unmatched is `unknown` — the ordinary failure road.
|
|
23
|
+
* - A sign-in that no longer works (`auth-expired`) is recognized FIRST,
|
|
24
|
+
* for every provider and version: an expired or revoked login is not a
|
|
25
|
+
* property of a CLI version, and retrying it only burns attempts. It is
|
|
26
|
+
* never fallback-eligible — a lapsed login must not reach a paid key.
|
|
23
27
|
*/
|
|
24
28
|
import type { ProviderId } from "./provider.js";
|
|
25
29
|
export type TerminalClass =
|
|
@@ -29,6 +33,9 @@ export type TerminalClass =
|
|
|
29
33
|
| "credits-depleted"
|
|
30
34
|
/** Temporary throttling NOT tied to the usage cap — backoff, NEVER paid fallback. */
|
|
31
35
|
| "transient-throttle"
|
|
36
|
+
/** The provider's sign-in or API key no longer works — NO retry, NO
|
|
37
|
+
* fallback: dispatch for that provider pauses until someone signs in. */
|
|
38
|
+
| "auth-expired"
|
|
32
39
|
/** A definite non-exhaustion terminal (ordinary failure). */
|
|
33
40
|
| "not-exhausted"
|
|
34
41
|
/** No structural signal matched — the ordinary failure/strike road. */
|
|
@@ -82,4 +89,11 @@ export declare function classifyTerminal(args: {
|
|
|
82
89
|
version: string | null;
|
|
83
90
|
authMode: "subscription" | "api-key";
|
|
84
91
|
terminal: StructuralTerminal | null;
|
|
92
|
+
/** What a run that failed within seconds, having produced nothing, said
|
|
93
|
+
* on its way out (stderr and unstructured stdout) — read ONLY for the
|
|
94
|
+
* sign-in signal: a CLI that is not logged in often exits before its
|
|
95
|
+
* structured stream begins. */
|
|
96
|
+
earlyExit?: string | null;
|
|
85
97
|
}): TerminalClass;
|
|
98
|
+
/** Whether a provider's own words say its sign-in or key no longer works. */
|
|
99
|
+
export declare function isAuthFailure(text: string): boolean;
|
package/dist/exhaustion.js
CHANGED
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
* class. Exit code, bare rate-limit prose, timeout, notFound, stderr,
|
|
21
21
|
* and zero tokens NEVER independently establish exhaustion.
|
|
22
22
|
* - Everything unmatched is `unknown` — the ordinary failure road.
|
|
23
|
+
* - A sign-in that no longer works (`auth-expired`) is recognized FIRST,
|
|
24
|
+
* for every provider and version: an expired or revoked login is not a
|
|
25
|
+
* property of a CLI version, and retrying it only burns attempts. It is
|
|
26
|
+
* never fallback-eligible — a lapsed login must not reach a paid key.
|
|
23
27
|
*/
|
|
24
28
|
/** The two classes that may authorize a fallback advance. */
|
|
25
29
|
export const FALLBACK_ELIGIBLE = new Set([
|
|
@@ -111,6 +115,12 @@ export function classMatchesAuthMode(cls, authMode) {
|
|
|
111
115
|
*/
|
|
112
116
|
export function classifyTerminal(args) {
|
|
113
117
|
const { provider, version, authMode, terminal } = args;
|
|
118
|
+
// A sign-in that no longer works, before anything else and for any
|
|
119
|
+
// provider or version: no fixture is needed to know a login is gone.
|
|
120
|
+
if (terminal !== null && terminal.failed === true && isAuthFailure(`${terminal.code ?? ""}\n${terminal.text ?? ""}`))
|
|
121
|
+
return "auth-expired";
|
|
122
|
+
if (typeof args.earlyExit === "string" && isAuthFailure(args.earlyExit))
|
|
123
|
+
return "auth-expired";
|
|
114
124
|
if (terminal === null)
|
|
115
125
|
return "unknown";
|
|
116
126
|
// The classifier's contract permits failed:false, but a non-failure
|
|
@@ -139,3 +149,28 @@ export function classifyTerminal(args) {
|
|
|
139
149
|
// unknown above).
|
|
140
150
|
return "not-exhausted";
|
|
141
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* The sign-in signals, provider-neutral: Claude's expired or unrefreshable
|
|
154
|
+
* OAuth session and its "Please run /login", an API key the provider calls
|
|
155
|
+
* invalid or revoked, Codex's and Gemini's "not logged in", and an HTTP 401
|
|
156
|
+
* the harness reports as its terminal. Ordinary failures (a test that
|
|
157
|
+
* failed, a timeout, a usage limit) match none of these.
|
|
158
|
+
*/
|
|
159
|
+
const AUTH_FAILURE = [
|
|
160
|
+
// Provider-specific phrasings only: a build's own output (a login feature's
|
|
161
|
+
// tests, an HTTP 401 in a log) must never read as the agent's sign-in.
|
|
162
|
+
/\bOAuth (?:session|token)\b[^\n]{0,80}\b(?:expired|could not be refreshed|revoked|invalid)/i,
|
|
163
|
+
/\bFailed to authenticate\b[^\n]{0,40}\b(?:OAuth|token|session|credentials|API key)/i,
|
|
164
|
+
/\bPlease run \/login\b/i,
|
|
165
|
+
/\binvalid[ _-](?:x-)?api[ _-]?key\b/i,
|
|
166
|
+
/\binvalid bearer token\b/i,
|
|
167
|
+
/\bapi[ _-]?key\b[^\n]{0,40}\b(?:is )?(?:invalid|revoked|expired|not valid)\b/i,
|
|
168
|
+
/"type"\s*:\s*"authentication_error"/i,
|
|
169
|
+
/\bMissing bearer (?:or basic )?authentication\b/i,
|
|
170
|
+
/^Not logged in\b/m,
|
|
171
|
+
];
|
|
172
|
+
/** Whether a provider's own words say its sign-in or key no longer works. */
|
|
173
|
+
export function isAuthFailure(text) {
|
|
174
|
+
const bounded = text.slice(0, 8192);
|
|
175
|
+
return AUTH_FAILURE.some(re => re.test(bounded));
|
|
176
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How this Toolroll was installed, read from where its bin really lives, and
|
|
3
|
+
* the one command that updates it that way. Only the path is read: nothing is
|
|
4
|
+
* run and nothing leaves the computer.
|
|
5
|
+
*/
|
|
6
|
+
export type InstallKind = "npm" | "pnpm" | "bun" | "yarn" | "homebrew" | "npx" | "source";
|
|
7
|
+
export type InstallMethod = {
|
|
8
|
+
kind: InstallKind;
|
|
9
|
+
updateCommand: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const UPDATE_COMMANDS: Record<InstallKind, string>;
|
|
12
|
+
export type InstallProbe = {
|
|
13
|
+
realpath?: (path: string) => string;
|
|
14
|
+
exists?: (path: string) => boolean;
|
|
15
|
+
};
|
|
16
|
+
/** The install method of the bin at `bin` (the running one by default). Unknown shapes read as npm, the common case. */
|
|
17
|
+
export declare function installMethod(bin?: string | undefined, probe?: InstallProbe): InstallMethod;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How this Toolroll was installed, read from where its bin really lives, and
|
|
3
|
+
* the one command that updates it that way. Only the path is read: nothing is
|
|
4
|
+
* run and nothing leaves the computer.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
export const UPDATE_COMMANDS = {
|
|
9
|
+
npm: "npm install -g toolroll@latest",
|
|
10
|
+
pnpm: "pnpm add -g toolroll@latest",
|
|
11
|
+
bun: "bun add -g toolroll@latest",
|
|
12
|
+
yarn: "yarn global add toolroll@latest",
|
|
13
|
+
homebrew: "brew upgrade ap9000/toolroll/toolroll",
|
|
14
|
+
npx: "npx toolroll@latest",
|
|
15
|
+
source: "git pull && npm install && npm run build",
|
|
16
|
+
};
|
|
17
|
+
/** The install method of the bin at `bin` (the running one by default). Unknown shapes read as npm, the common case. */
|
|
18
|
+
export function installMethod(bin = process.argv[1], probe = {}) {
|
|
19
|
+
const kind = installKind(bin, probe);
|
|
20
|
+
return { kind, updateCommand: UPDATE_COMMANDS[kind] };
|
|
21
|
+
}
|
|
22
|
+
function installKind(bin, probe) {
|
|
23
|
+
if (bin === undefined || bin === "")
|
|
24
|
+
return "npm";
|
|
25
|
+
let real = bin;
|
|
26
|
+
try {
|
|
27
|
+
real = (probe.realpath ?? realpathSync)(bin);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// A bin that cannot be resolved is judged by the path it was run by.
|
|
31
|
+
}
|
|
32
|
+
const path = real.replace(/\\/g, "/");
|
|
33
|
+
// A plane deployed from a checkout by scripts/deploy-browser.mjs runs from staged-upgrades/browser-*: it updates
|
|
34
|
+
// by deploying from source, never from npm.
|
|
35
|
+
if (path.includes("/staged-upgrades/browser-"))
|
|
36
|
+
return "source";
|
|
37
|
+
if (/\/Cellar\/toolroll\//i.test(path))
|
|
38
|
+
return "homebrew";
|
|
39
|
+
if (path.includes("/_npx/"))
|
|
40
|
+
return "npx";
|
|
41
|
+
if (/\/\.bun\/install\/global\//.test(path))
|
|
42
|
+
return "bun";
|
|
43
|
+
if (/\/pnpm\/global\//i.test(path))
|
|
44
|
+
return "pnpm";
|
|
45
|
+
if (/\/(?:\.config\/yarn|yarn\/data)\/global\//i.test(path))
|
|
46
|
+
return "yarn";
|
|
47
|
+
if (path.includes("/node_modules/"))
|
|
48
|
+
return "npm";
|
|
49
|
+
// Not inside node_modules: a checkout, when the package's own folder is a git working copy.
|
|
50
|
+
const exists = probe.exists ?? existsSync;
|
|
51
|
+
let folder = dirname(real);
|
|
52
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
53
|
+
if (exists(join(folder, "package.json")) && exists(join(folder, ".git")))
|
|
54
|
+
return "source";
|
|
55
|
+
const parent = dirname(folder);
|
|
56
|
+
if (parent === folder)
|
|
57
|
+
break;
|
|
58
|
+
folder = parent;
|
|
59
|
+
}
|
|
60
|
+
return "npm";
|
|
61
|
+
}
|
package/dist/invoke.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { adapterFor, auditOf } from "./provider.js";
|
|
20
20
|
import { readProviderKey, readAuthModeStrict, PROVIDER_KEY_ENV, OWN_KEY_ENV } from "./keys.js";
|
|
21
21
|
import { classifyTerminal } from "./exhaustion.js";
|
|
22
|
+
import { liftAuthPause } from "./provider-auth.js";
|
|
22
23
|
import { attestProvider } from "./attest.js";
|
|
23
24
|
import { run as runCommand, runOwnerTag, startClaudeHeldSession } from "./exec.js";
|
|
24
25
|
import { currentContainment } from "./containment.js";
|
|
@@ -32,6 +33,9 @@ import { billingOf, claudeBillingFrom, seenBilling } from "./spend.js";
|
|
|
32
33
|
/** The claude binary name — kept for the legacy quota rows and tests that
|
|
33
34
|
* describe history; new code carries a resolved AgentSpec instead. */
|
|
34
35
|
export const PROVIDER_BINARY = "claude";
|
|
36
|
+
/** "Within seconds": how soon a silent failed run must end for its stderr to
|
|
37
|
+
* be read for the sign-in signal. */
|
|
38
|
+
const AUTH_EARLY_EXIT_MS = 30_000;
|
|
35
39
|
/**
|
|
36
40
|
* A provider can run the repository's own CLI. In a self-hosting build that
|
|
37
41
|
* must never mean "open the database that launched me": a migration from the
|
|
@@ -306,6 +310,7 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
306
310
|
? await codexLoginBilling(attested !== null ? attested.executable : adapter.binary, [...(runOptions.omitEnv ?? []), ...adapter.extraOmitEnv, ...ownKeyEnv])
|
|
307
311
|
: null;
|
|
308
312
|
let result;
|
|
313
|
+
const spawnedAt = Date.now();
|
|
309
314
|
try {
|
|
310
315
|
// Under the stop watch (v52): the run's stop row is re-read while the
|
|
311
316
|
// provider runs, and a stop kills THIS run's process group through the
|
|
@@ -438,11 +443,20 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
438
443
|
// no version here yet (attested === null), so they classify fail-closed
|
|
439
444
|
// until their exhaustion fixture — and the version proving it needs — is
|
|
440
445
|
// captured and reviewed.
|
|
446
|
+
// A run that failed within seconds having produced nothing is read for
|
|
447
|
+
// the sign-in signal too: a CLI that is not logged in often says so on
|
|
448
|
+
// stderr before its structured stream starts.
|
|
449
|
+
const quietEarlyExit = result.code !== 0 && !result.timedOut && !result.notFound &&
|
|
450
|
+
Date.now() - spawnedAt <= AUTH_EARLY_EXIT_MS &&
|
|
451
|
+
envelope.promptConsumed !== true && (envelope.tokensOut ?? 0) === 0;
|
|
441
452
|
const terminalClass = classifyTerminal({
|
|
442
453
|
provider: spec.provider,
|
|
443
454
|
version: attested === null ? null : attested.version,
|
|
444
455
|
authMode,
|
|
445
456
|
terminal: envelope.structuralTerminal,
|
|
457
|
+
// stderr and plain stdout only: a JSON event line can carry the agent's
|
|
458
|
+
// own command output (codex aggregated_output), never its sign-in.
|
|
459
|
+
earlyExit: quietEarlyExit ? `${result.stderr.slice(-4096)}\n${plainLines(result.stdout).slice(-4096)}` : null,
|
|
446
460
|
});
|
|
447
461
|
store.stampTerminalClass(runId, authMode, terminalClass);
|
|
448
462
|
// Contradictory structural identity is a provider protocol failure, not
|
|
@@ -519,6 +533,14 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
519
533
|
finalMessage: envelope.finalMessage,
|
|
520
534
|
};
|
|
521
535
|
}
|
|
536
|
+
// A run that worked proves its provider's sign-in works: a sign-in pause
|
|
537
|
+
// on it lifts, and its waiting tasks may start again.
|
|
538
|
+
if (result.code === 0 && envelope.structuralTerminal === null && envelope.promptConsumed !== false) {
|
|
539
|
+
try {
|
|
540
|
+
liftAuthPause(store, spec.provider, "run", `run #${runId}`, clock(), new Date(spawnedAt));
|
|
541
|
+
}
|
|
542
|
+
catch { /* the pause stays until the next proof */ }
|
|
543
|
+
}
|
|
522
544
|
return {
|
|
523
545
|
kind: "ran",
|
|
524
546
|
outcome: {
|
|
@@ -778,3 +800,7 @@ async function codexLoginBilling(binary, omitEnv) {
|
|
|
778
800
|
return null;
|
|
779
801
|
}
|
|
780
802
|
}
|
|
803
|
+
/** The lines of a harness's stdout that are not JSON events. */
|
|
804
|
+
function plainLines(stdout) {
|
|
805
|
+
return stdout.split("\n").filter(line => !line.trimStart().startsWith("{")).join("\n");
|
|
806
|
+
}
|
package/dist/lead-status.d.ts
CHANGED
|
@@ -62,6 +62,13 @@ export type InstallationStatus = {
|
|
|
62
62
|
plan: string | null;
|
|
63
63
|
observedAt: string;
|
|
64
64
|
})[];
|
|
65
|
+
/** Providers whose sign-in stopped working: their dispatch is paused. */
|
|
66
|
+
signIn: {
|
|
67
|
+
provider: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
command: string;
|
|
70
|
+
since: string;
|
|
71
|
+
}[];
|
|
65
72
|
};
|
|
66
73
|
/** One indexed observation for `task wait`. `watchedRun` is the attempt the
|
|
67
74
|
* caller first saw. Each poll follows the task's newest attempt from there, so
|
package/dist/lead-status.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* this module never opens a repository, retained evidence, or a live log. */
|
|
3
3
|
import { COMPLETION_ACTION } from "./result-completion.js";
|
|
4
4
|
import { windowLabel } from "./provider-limits.js";
|
|
5
|
+
import { openAuthPauses, signInCommand, signInReason } from "./provider-auth.js";
|
|
5
6
|
import { BUILT_IN, checkSuitesOf } from "./store.js";
|
|
6
7
|
const maybeNumber = (value) => value === null || value === undefined || !Number.isInteger(Number(value)) ? null : Number(value);
|
|
7
8
|
const CHECK_COLUMNS = "rc.status AS check_status, rc.exit_code AS check_exit_code, rc.suites_json AS check_suites_json";
|
|
@@ -192,8 +193,16 @@ export function installationStatus(store, now) {
|
|
|
192
193
|
WHERE task.state = 'queued'`).all(now.toISOString(), BUILT_IN);
|
|
193
194
|
const reasons = new Map();
|
|
194
195
|
const queuedTasks = [];
|
|
196
|
+
const pauses = openAuthPauses(store);
|
|
195
197
|
for (const row of queuedRows) {
|
|
196
|
-
|
|
198
|
+
let reason = queueReason(row);
|
|
199
|
+
if (reason === "ready for a worker" && pauses.length > 0) {
|
|
200
|
+
const scope = store.getScope(String(row["id"]));
|
|
201
|
+
const provider = scope?.approvedProfile?.provider ?? scope?.profile?.provider;
|
|
202
|
+
const paused = pauses.find(one => one.provider === provider);
|
|
203
|
+
if (paused !== undefined)
|
|
204
|
+
reason = signInReason(paused);
|
|
205
|
+
}
|
|
197
206
|
reasons.set(reason, (reasons.get(reason) ?? 0) + 1);
|
|
198
207
|
if (queuedTasks.length < 8)
|
|
199
208
|
queuedTasks.push({ task: String(row["id"]), reason });
|
|
@@ -243,10 +252,14 @@ export function installationStatus(store, now) {
|
|
|
243
252
|
reached: Number(row["reached"]) === 1,
|
|
244
253
|
observedAt: String(row["observed_at"]),
|
|
245
254
|
})),
|
|
255
|
+
signIn: pauses.map(one => ({ provider: one.provider, reason: signInReason(one), command: signInCommand(one), since: one.openedAt })),
|
|
246
256
|
};
|
|
247
257
|
}
|
|
248
258
|
export function renderInstallationStatus(status) {
|
|
249
259
|
const lines = [];
|
|
260
|
+
// A paused provider comes first: it is the one thing a person must do.
|
|
261
|
+
for (const one of status.signIn)
|
|
262
|
+
lines.push(`${one.reason} — run \`${one.command}\`. Its tasks wait until then.`);
|
|
250
263
|
lines.push(status.running.count === 0 ? "Running: none" : `Running: ${status.running.count} — ${status.running.tasks.map(one => `${one.task} (#${one.run}, ${one.phase})`).join(", ")}${status.running.count > status.running.tasks.length ? ", …" : ""}`);
|
|
251
264
|
lines.push(status.queued.count === 0 ? "Queued: none" : `Queued: ${status.queued.count} — ${status.queued.tasks.map(one => `${one.task} (${one.reason})`).join(", ")}${status.queued.count > status.queued.tasks.length ? ", …" : ""}`);
|
|
252
265
|
lines.push(status.waitingForReview.count === 0 ? "Ready for review: none" : `Ready for review: ${status.waitingForReview.count} — ${status.waitingForReview.results.map(one => `${one.task} (#${one.run})`).join(", ")}${status.waitingForReview.count > status.waitingForReview.results.length ? ", …" : ""}`);
|
|
@@ -276,7 +289,7 @@ export function renderInstallationStatus(status) {
|
|
|
276
289
|
lines.push(`Plan windows: ${provider} — ${detail}`);
|
|
277
290
|
}
|
|
278
291
|
}
|
|
279
|
-
return lines.slice(0, 12);
|
|
292
|
+
return lines.slice(0, 12 + status.signIn.length);
|
|
280
293
|
}
|
|
281
294
|
export function renderTaskWait(snapshot, outcome = snapshot.outcome) {
|
|
282
295
|
const check = snapshot.check.status === "unknown" ? "checks unknown" : snapshot.check.status === "not-run" ? "checks not run" : `checks ${snapshot.check.status}${snapshot.check.exitCode === null ? "" : ` (exit ${snapshot.check.exitCode})`}`;
|
package/dist/mate.js
CHANGED
|
@@ -410,10 +410,13 @@ export async function runMateTurn(input) {
|
|
|
410
410
|
const outcome = executeMateTool({ store, who, now: clock(), draft, selectEvidence, step: steps, readDecisions, readResults, ...(input.evidenceRoot === undefined ? {} : { evidenceRoot: input.evidenceRoot }), ...(input.mediaDelivery === undefined ? {} : { mediaDelivery: input.mediaDelivery }) }, call.name, call.args, view);
|
|
411
411
|
if (READ_TOOLS.has(call.name))
|
|
412
412
|
reads++;
|
|
413
|
-
// Opt-in, local diagnostics for end-to-end runs: what the lead asked of each tool and what came back, keys
|
|
413
|
+
// Opt-in, local diagnostics for end-to-end runs: what the lead asked of each tool and what came back (its start), keys
|
|
414
|
+
// blanked. Each line stays whole JSON so a run can assert on what a tool returned rather than on the model's words.
|
|
414
415
|
if (envValue(process.env, "MATE_TRACE") === "1") {
|
|
415
416
|
const blanked = (_, value) => typeof value === "string" ? redactSecretAssignments(redactSecretLines(value, scanForSecrets(value))) : value;
|
|
416
|
-
|
|
417
|
+
const args = JSON.stringify(call.args, blanked).slice(0, 1500);
|
|
418
|
+
const result = JSON.stringify(outcome.ok ? outcome.body : null, blanked)?.slice(0, 2000) ?? null;
|
|
419
|
+
process.stderr.write(`mate-trace ${JSON.stringify({ turn: turnId, step: steps, tool: call.name, args: args.length < 1500 ? call.args : args, ok: outcome.ok, ...(outcome.ok ? { result } : { message: outcome.message }) }, blanked)}\n`);
|
|
417
420
|
}
|
|
418
421
|
history.push({ role: "tool", callId: call.id, name: call.name, result: capped(outcome.ok ? outcome.body : { ok: false, message: outcome.message }) });
|
|
419
422
|
}
|
package/dist/operate.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { type TelegramTransport } from "./telegram.js";
|
|
|
6
6
|
import { type MateCliSeams } from "./mate-cli.js";
|
|
7
7
|
import { type PublishExec } from "./publish.js";
|
|
8
8
|
import { type Runner as CommandRunner } from "./builder.js";
|
|
9
|
+
import { type ReleaseIo } from "./releases.js";
|
|
9
10
|
export type Write = (line: string) => void;
|
|
10
11
|
/**
|
|
11
12
|
* 0 done · 1 broke · 2 bad usage · 3 ran fine, the answer is no.
|
|
@@ -33,6 +34,10 @@ export type OperateOptions = {
|
|
|
33
34
|
additionalProjectRepos?: () => readonly string[];
|
|
34
35
|
/** Overridden by tests and by an agent that wants its own queue. */
|
|
35
36
|
databaseFile?: string;
|
|
37
|
+
/** Injected by tests: how `status` and the console ask for the latest release. */
|
|
38
|
+
releaseIo?: ReleaseIo;
|
|
39
|
+
/** Injected by tests: the bin whose real path says how Toolroll was installed. */
|
|
40
|
+
installBin?: string;
|
|
36
41
|
openDatabase?: (file: string) => Store;
|
|
37
42
|
now?: Date;
|
|
38
43
|
/**
|