toolroll 0.9.96 → 0.9.98
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/CHANGELOG.md +18 -0
- package/dist/approval-authority.d.ts +10 -1
- package/dist/approval-authority.js +18 -2
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-workspace.d.ts +6 -3
- package/dist/chat-actions.d.ts +40 -5
- package/dist/chat-actions.js +124 -13
- package/dist/chat-card.js +4 -1
- package/dist/chat-channel.d.ts +3 -0
- package/dist/chat-channel.js +24 -7
- package/dist/chat-decide.js +4 -2
- package/dist/chat-delivery.d.ts +5 -1
- package/dist/chat-delivery.js +74 -25
- package/dist/chat-follow.d.ts +6 -3
- package/dist/chat-follow.js +23 -7
- package/dist/chat-live-schema.d.ts +17 -0
- package/dist/chat-live-schema.js +32 -0
- package/dist/chat-live.d.ts +161 -0
- package/dist/chat-live.js +491 -0
- package/dist/chat-task-actions.js +1 -1
- package/dist/chat-terms.d.ts +8 -0
- package/dist/chat-terms.js +14 -0
- package/dist/chat-voice.js +1 -1
- package/dist/contracts/chat-actions.d.ts +6 -0
- package/dist/contracts/chat-actions.js +2 -0
- package/dist/contracts/cli.d.ts +6 -0
- package/dist/contracts/cli.js +3 -0
- package/dist/contracts/console-api.d.ts +1 -0
- package/dist/contracts/console-api.js +1 -0
- package/dist/contracts/lead-tools.d.ts +17 -0
- package/dist/contracts/lead-tools.js +4 -1
- package/dist/contracts/store-json.d.ts +1 -0
- package/dist/discord-api.d.ts +3 -1
- package/dist/discord-api.js +9 -3
- package/dist/discord-chat.d.ts +13 -3
- package/dist/discord-chat.js +77 -21
- package/dist/discord.js +8 -1
- package/dist/fast-start.d.ts +83 -6
- package/dist/fast-start.js +112 -15
- package/dist/home-groups.d.ts +39 -0
- package/dist/home-groups.js +33 -0
- package/dist/lead-answer-eval.d.ts +93 -0
- package/dist/lead-answer-eval.js +259 -0
- package/dist/lead-capabilities.js +1 -1
- package/dist/lead-cli-live.d.ts +56 -0
- package/dist/lead-cli-live.js +200 -0
- package/dist/lead-cli.d.ts +3 -0
- package/dist/lead-cli.js +34 -4
- package/dist/lead-context.js +2 -2
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +10 -7
- package/dist/lead-doors.d.ts +32 -0
- package/dist/lead-doors.js +80 -14
- package/dist/lead-latency-schema.d.ts +24 -0
- package/dist/lead-latency-schema.js +36 -0
- package/dist/lead-progress.d.ts +10 -1
- package/dist/lead-progress.js +1 -1
- package/dist/lead-queue-runtime.d.ts +1 -1
- package/dist/lead-queue-runtime.js +1 -1
- package/dist/lead-route.d.ts +56 -0
- package/dist/lead-route.js +104 -0
- package/dist/lead-status.d.ts +3 -0
- package/dist/lead-status.js +8 -3
- package/dist/lead-tools.d.ts +20 -0
- package/dist/lead-tools.js +66 -3
- package/dist/lead-voice.js +1 -1
- package/dist/lead.d.ts +4 -2
- package/dist/lead.js +97 -28
- package/dist/live-reply.d.ts +83 -0
- package/dist/live-reply.js +302 -0
- package/dist/local-owner.js +2 -0
- package/dist/one-step-chat-schema.d.ts +22 -0
- package/dist/one-step-chat-schema.js +37 -0
- package/dist/operate.d.ts +27 -1
- package/dist/operate.js +245 -33
- package/dist/project-delete.js +9 -0
- package/dist/project-knowledge.d.ts +10 -10
- package/dist/publish.d.ts +2 -0
- package/dist/publish.js +1 -1
- package/dist/pull-request-flow.d.ts +4 -0
- package/dist/pull-request-flow.js +16 -1
- package/dist/reply-shape.d.ts +9 -0
- package/dist/reply-shape.js +13 -0
- package/dist/reply-voice.d.ts +23 -0
- package/dist/reply-voice.js +37 -0
- package/dist/scope.d.ts +6 -1
- package/dist/scope.js +3 -2
- package/dist/serve.js +11 -4
- package/dist/server/chat.d.ts +2 -1
- package/dist/server/chat.js +22 -10
- package/dist/server/chrome.js +2 -2
- package/dist/server/home.d.ts +4 -3
- package/dist/server/home.js +33 -28
- package/dist/server/live.js +18 -21
- package/dist/server/render-chat.d.ts +5 -0
- package/dist/server/render-chat.js +15 -8
- package/dist/server/render-pages.js +2 -2
- package/dist/server/render-settings.d.ts +8 -0
- package/dist/server/render-settings.js +13 -2
- package/dist/server/route-table.js +2 -0
- package/dist/server/settings.js +14 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-api.js +11 -0
- package/dist/slack-chat.d.ts +11 -1
- package/dist/slack-chat.js +50 -5
- package/dist/slack-settings.js +8 -1
- package/dist/slack.js +10 -3
- package/dist/store.d.ts +33 -2
- package/dist/store.js +92 -16
- package/dist/subscription-chat.d.ts +2 -0
- package/dist/subscription-chat.js +3 -1
- package/dist/teams-chat.d.ts +9 -2
- package/dist/teams-chat.js +50 -2
- package/dist/teams.js +5 -2
- package/dist/telegram-lead.d.ts +3 -0
- package/dist/telegram-lead.js +132 -14
- package/dist/telegram-live.d.ts +116 -0
- package/dist/telegram-live.js +224 -0
- package/dist/telegram-status.js +9 -4
- package/dist/telegram.d.ts +12 -0
- package/dist/telegram.js +142 -10
- package/dist/toolroll-update.js +6 -1
- package/dist/tui-contract.d.ts +2 -0
- package/dist/tui-source.js +3 -0
- package/dist/tui.js +41 -41
- package/dist/typed-start.d.ts +51 -0
- package/dist/typed-start.js +98 -0
- package/dist/wake-hub.d.ts +72 -0
- package/dist/wake-hub.js +151 -0
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v158: the lead's latency records and its quick-answer switch (docs/plans/responsive-chat.md, slice 5). Adds only:
|
|
3
|
+
* a saved row reads as it did. Kept beside nothing else so the update road can read the lists without the store.
|
|
4
|
+
*
|
|
5
|
+
* - `chat_turn.tools`: the tool names one lead step asked for, as a JSON array of names (never arguments); saved rows
|
|
6
|
+
* read NULL (not recorded). So the next latency decision (a prefetched brief, §7.2) is made from data.
|
|
7
|
+
* - `chat_turn.failure_detail` and `mate_turn.failure_detail`: why a step or reply failed, more exactly than
|
|
8
|
+
* failure_reason (LEAD_FAILURE_DETAILS); saved rows read NULL (unknown), and read the plain reason's words as before.
|
|
9
|
+
* - `mate_turn.route`: which model answered (§8): `default`, `fast` (the quick model answered) or `escalated` (a quick
|
|
10
|
+
* step asked for more than reading, so the main model took the reply over); saved rows read NULL (before routing).
|
|
11
|
+
* - `lead_quick_setting`: Settings → Lead's "Quick answers use a faster model", one row; absent reads off.
|
|
12
|
+
*/
|
|
13
|
+
export declare const LEAD_FAILURE_DETAILS: readonly ["not-installed", "sign-in", "refused", "lost", "timeout-silent", "timeout-output"];
|
|
14
|
+
export type LeadFailureDetail = typeof LEAD_FAILURE_DETAILS[number];
|
|
15
|
+
export declare const LEAD_ROUTES: readonly ["default", "fast", "escalated"];
|
|
16
|
+
export type LeadRoute = typeof LEAD_ROUTES[number];
|
|
17
|
+
export declare const V158_SCHEMA = "\nCREATE TABLE IF NOT EXISTS lead_quick_setting (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n enabled INTEGER NOT NULL CHECK (enabled IN (0, 1)),\n updated_by TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n";
|
|
18
|
+
export declare const V158_ADDED_COLUMNS: Readonly<Record<string, Readonly<Record<string, {
|
|
19
|
+
definition: string;
|
|
20
|
+
saved: string;
|
|
21
|
+
}>>>>;
|
|
22
|
+
export declare const V158_TABLES: readonly ["lead_quick_setting"];
|
|
23
|
+
/** The v158 migration's steps, in order (a test seam may stop after each). */
|
|
24
|
+
export declare const V158_STEPS: readonly ["lead-quick-setting", "lead-latency-columns"];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v158: the lead's latency records and its quick-answer switch (docs/plans/responsive-chat.md, slice 5). Adds only:
|
|
3
|
+
* a saved row reads as it did. Kept beside nothing else so the update road can read the lists without the store.
|
|
4
|
+
*
|
|
5
|
+
* - `chat_turn.tools`: the tool names one lead step asked for, as a JSON array of names (never arguments); saved rows
|
|
6
|
+
* read NULL (not recorded). So the next latency decision (a prefetched brief, §7.2) is made from data.
|
|
7
|
+
* - `chat_turn.failure_detail` and `mate_turn.failure_detail`: why a step or reply failed, more exactly than
|
|
8
|
+
* failure_reason (LEAD_FAILURE_DETAILS); saved rows read NULL (unknown), and read the plain reason's words as before.
|
|
9
|
+
* - `mate_turn.route`: which model answered (§8): `default`, `fast` (the quick model answered) or `escalated` (a quick
|
|
10
|
+
* step asked for more than reading, so the main model took the reply over); saved rows read NULL (before routing).
|
|
11
|
+
* - `lead_quick_setting`: Settings → Lead's "Quick answers use a faster model", one row; absent reads off.
|
|
12
|
+
*/
|
|
13
|
+
export const LEAD_FAILURE_DETAILS = ["not-installed", "sign-in", "refused", "lost", "timeout-silent", "timeout-output"];
|
|
14
|
+
export const LEAD_ROUTES = ["default", "fast", "escalated"];
|
|
15
|
+
const detailCheck = (column) => `TEXT CHECK (${column} IS NULL OR ${column} IN (${LEAD_FAILURE_DETAILS.map(one => `'${one}'`).join(",")}))`;
|
|
16
|
+
export const V158_SCHEMA = `
|
|
17
|
+
CREATE TABLE IF NOT EXISTS lead_quick_setting (
|
|
18
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
19
|
+
enabled INTEGER NOT NULL CHECK (enabled IN (0, 1)),
|
|
20
|
+
updated_by TEXT NOT NULL,
|
|
21
|
+
updated_at TEXT NOT NULL
|
|
22
|
+
);
|
|
23
|
+
`;
|
|
24
|
+
export const V158_ADDED_COLUMNS = Object.freeze({
|
|
25
|
+
chat_turn: {
|
|
26
|
+
tools: { definition: "TEXT", saved: "NULL" },
|
|
27
|
+
failure_detail: { definition: detailCheck("failure_detail"), saved: "NULL" },
|
|
28
|
+
},
|
|
29
|
+
mate_turn: {
|
|
30
|
+
route: { definition: `TEXT CHECK (route IS NULL OR route IN (${LEAD_ROUTES.map(one => `'${one}'`).join(",")}))`, saved: "NULL" },
|
|
31
|
+
failure_detail: { definition: detailCheck("failure_detail"), saved: "NULL" },
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
export const V158_TABLES = Object.freeze(["lead_quick_setting"]);
|
|
35
|
+
/** The v158 migration's steps, in order (a test seam may stop after each). */
|
|
36
|
+
export const V158_STEPS = Object.freeze(["lead-quick-setting", "lead-latency-columns"]);
|
package/dist/lead-progress.d.ts
CHANGED
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
export type LeadProgress = {
|
|
5
5
|
kind: "started";
|
|
6
6
|
turn: number;
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
|
+
/** `reason`: the step re-asks the model about its own reply (a repair of a claim it can't back, or a shorten). */
|
|
9
|
+
| {
|
|
8
10
|
kind: "step";
|
|
9
11
|
turn: number;
|
|
10
12
|
step: number;
|
|
13
|
+
reason?: "repair" | "shorten";
|
|
11
14
|
} | {
|
|
12
15
|
kind: "tool";
|
|
13
16
|
turn: number;
|
|
@@ -28,6 +31,12 @@ export type LeadProgress = {
|
|
|
28
31
|
step: number;
|
|
29
32
|
text: string;
|
|
30
33
|
final?: true;
|
|
34
|
+
}
|
|
35
|
+
/** Text was held back because it looked like a password or key (mid-stream, or the finished reply refused): every
|
|
36
|
+
* live preview of this turn closes and clears at once (docs/plans/responsive-chat.md §2.1). */
|
|
37
|
+
| {
|
|
38
|
+
kind: "blocked";
|
|
39
|
+
turn: number;
|
|
31
40
|
};
|
|
32
41
|
export type LeadToolOutcome = {
|
|
33
42
|
state: "succeeded";
|
package/dist/lead-progress.js
CHANGED
|
@@ -48,7 +48,7 @@ const TOOL_LABELS = {
|
|
|
48
48
|
export function leadToolLabel(name) {
|
|
49
49
|
if (name in TOOL_LABELS)
|
|
50
50
|
return TOOL_LABELS[name];
|
|
51
|
-
return name.startsWith("propose_") ? "Preparing a card for you to confirm" : "Working";
|
|
51
|
+
return name.startsWith("propose_") ? "Preparing a card for you to confirm" : "Working on it";
|
|
52
52
|
}
|
|
53
53
|
/** Tool errors instruct the model how to retry. Only known, fixed copy belongs
|
|
54
54
|
* in the live UI; never echo arguments, redaction placeholders or exceptions. */
|
|
@@ -28,7 +28,7 @@ export type PersonalQueueRuntimeOptions = {
|
|
|
28
28
|
};
|
|
29
29
|
/** The console's live reply display: started per turn, ended with the progress handle it returned. */
|
|
30
30
|
live?: {
|
|
31
|
-
begin: (thread: number) => (event: LeadProgress) => void;
|
|
31
|
+
begin: (thread: number, asked: string) => (event: LeadProgress) => void;
|
|
32
32
|
end: (thread: number, ok: boolean, progress: (event: LeadProgress) => void) => void;
|
|
33
33
|
};
|
|
34
34
|
subscriptionRunner?: LeadTurnInput["subscriptionRunner"];
|
|
@@ -105,7 +105,7 @@ export function createPersonalQueueRuntime(options) {
|
|
|
105
105
|
onAdmitted: turn => {
|
|
106
106
|
if (!queue.bindTurn(claim, turn))
|
|
107
107
|
throw new Error("The queued message changed before admission.");
|
|
108
|
-
progress = options.live?.begin(claim.thread);
|
|
108
|
+
progress = options.live?.begin(claim.thread, claim.text);
|
|
109
109
|
},
|
|
110
110
|
...(context === undefined ? {} : { context }), channel: "console", clock, evidenceRoot: options.evidenceRoot,
|
|
111
111
|
...(options.live === undefined ? {} : { onProgress: (event) => progress?.(event) }),
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { LeadRoute } from "./lead-latency-schema.js";
|
|
2
|
+
import type { Store } from "./store.js";
|
|
3
|
+
/** Words that ask for something to be done (§8's fixed list, plus a few plain synonyms). Any of them keeps a message on
|
|
4
|
+
* the main model. Matched as whole words in their plain form, the form a request takes ("fix it", "can you merge",
|
|
5
|
+
* "file a task"): "what's running?" or "is it merged?" ask about a state and stay quick. A quick step that asks to act
|
|
6
|
+
* anyway is escalated (quickStepEscalates), so this list is the first line, not the only one. */
|
|
7
|
+
export declare const QUICK_ACT_WORDS: readonly string[];
|
|
8
|
+
/** The longest message the quick model takes: longer ones are rarely a plain status question. */
|
|
9
|
+
export declare const QUICK_MESSAGE_MAX_CHARS = 240;
|
|
10
|
+
/** Whether a message reads as a status or read question with no act word (§8's message rule). */
|
|
11
|
+
export declare function quickQuestion(message: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The tools a quick reply's requests advertise: reads only. Every one is a read of saved state as this person sees it;
|
|
14
|
+
* none drafts, records a question, a promise, a memory or a card, or opens a page. `find_tools` is advertised beside them
|
|
15
|
+
* but is not one of them: it is how a quick step says it needs something else ("search before saying something can't
|
|
16
|
+
* be done"), so calling it hands the reply to the main model, unrun (more conservative than §8's "find_tools for
|
|
17
|
+
* something to change": any search escalates).
|
|
18
|
+
*/
|
|
19
|
+
export declare const QUICK_READ_TOOLS: ReadonlySet<string>;
|
|
20
|
+
/** Whether a quick step's request advertises this tool: the reads, and find_tools as the way to ask for more. */
|
|
21
|
+
export declare function quickAdvertises(name: string): boolean;
|
|
22
|
+
/** The faster model, as the model catalog names it: the newest Sonnet this computer's Claude has been seen to run, else
|
|
23
|
+
* the compiled current one. Always an exact id, never an alias that could move under a recorded route. */
|
|
24
|
+
export declare function quickLeadModel(store: Store): string;
|
|
25
|
+
export type QuickRouteInput = {
|
|
26
|
+
/** Settings → Lead's switch. */
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
provider: string;
|
|
29
|
+
/** The configured model, which a quick reply would otherwise run on. */
|
|
30
|
+
mainModel: string;
|
|
31
|
+
message: string;
|
|
32
|
+
/** Server-authored context for this turn (a task page, a crew update): present keeps the main model. */
|
|
33
|
+
hasContext: boolean;
|
|
34
|
+
/** The message replies to an image (a result's screenshot). */
|
|
35
|
+
hasReplyImage: boolean;
|
|
36
|
+
/** The thread's newest reply asked the owner a question. */
|
|
37
|
+
askOpen: boolean;
|
|
38
|
+
/** The faster model, and whether the organisation policy allows it. */
|
|
39
|
+
quickModel: string;
|
|
40
|
+
quickAllowed: boolean;
|
|
41
|
+
};
|
|
42
|
+
/** Which model answers: the faster one only when every condition holds. */
|
|
43
|
+
export declare function quickRoute(input: QuickRouteInput): {
|
|
44
|
+
route: Extract<LeadRoute, "fast">;
|
|
45
|
+
model: string;
|
|
46
|
+
} | {
|
|
47
|
+
route: Extract<LeadRoute, "default">;
|
|
48
|
+
};
|
|
49
|
+
/** Whether a quick step asked for anything beyond the quick reads: its calls are then discarded unrun (§8). */
|
|
50
|
+
export declare function quickStepEscalates(calls: readonly {
|
|
51
|
+
name: string;
|
|
52
|
+
}[]): boolean;
|
|
53
|
+
/** "Sonnet 5.5" for claude-sonnet-5-5; the id itself for anything else. */
|
|
54
|
+
export declare function modelName(model: string): string;
|
|
55
|
+
/** The activity line's words for a routed reply: "answered by Sonnet 5.5", "moved to Opus"; nothing for the default. */
|
|
56
|
+
export declare function routeWords(route: LeadRoute, quickModel: string, mainModel: string): string | null;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quick-answer router (docs/plans/responsive-chat.md §8, owner decision 7): which model answers a reply, decided
|
|
3
|
+
* before the turn, deterministically and conservatively. A reply goes to the faster model only when quick answers are
|
|
4
|
+
* on, the lead runs on the Claude membership, the organisation policy allows that model, the message carries no task
|
|
5
|
+
* or reply context, it doesn't answer the lead's own open question, and it reads as a status or read question with no
|
|
6
|
+
* act word. Everything else stays on the main model, unchanged.
|
|
7
|
+
*
|
|
8
|
+
* A quick reply is read-only by construction: its requests advertise only QUICK_READ_TOOLS (and find_tools, the way to
|
|
9
|
+
* ask for more). A quick step that asks for anything else has its calls discarded unrun, and the main model takes the reply over from that step (lead.ts).
|
|
10
|
+
*/
|
|
11
|
+
import { aliasTarget } from "./model-catalog.js";
|
|
12
|
+
import { exactModelId } from "./phase-routing.js";
|
|
13
|
+
import { SONNET } from "./model-choices.js";
|
|
14
|
+
/** Words that ask for something to be done (§8's fixed list, plus a few plain synonyms). Any of them keeps a message on
|
|
15
|
+
* the main model. Matched as whole words in their plain form, the form a request takes ("fix it", "can you merge",
|
|
16
|
+
* "file a task"): "what's running?" or "is it merged?" ask about a state and stay quick. A quick step that asks to act
|
|
17
|
+
* anyway is escalated (quickStepEscalates), so this list is the first line, not the only one. */
|
|
18
|
+
export const QUICK_ACT_WORDS = Object.freeze([
|
|
19
|
+
"file", "make", "create", "add", "fix", "change", "build", "plan", "approve", "confirm", "merge", "release", "ship",
|
|
20
|
+
"deploy", "cancel", "pause", "hold", "resume", "retry", "steer", "remember", "delete", "archive", "send", "run",
|
|
21
|
+
"start", "stop",
|
|
22
|
+
// Plain synonyms of the same acts.
|
|
23
|
+
"rerun", "redo", "undo", "revert", "push", "publish", "kill", "edit", "remove", "rename", "reset", "restart",
|
|
24
|
+
"install", "upgrade", "enable", "disable", "accept", "reject", "schedule", "assign", "write", "draft", "set", "move",
|
|
25
|
+
"close", "reopen", "unhold", "dismiss", "prioritize", "prioritise", "split", "revise",
|
|
26
|
+
]);
|
|
27
|
+
/** Openings that ask about the state of things. */
|
|
28
|
+
const ASKING_OPENINGS = new Set([
|
|
29
|
+
"what", "what's", "whats", "which", "who", "who's", "whose", "why", "when", "where", "how", "how's", "is", "are", "was",
|
|
30
|
+
"were", "did", "does", "do", "has", "have", "had", "any", "anything", "show", "list", "status", "tell", "give",
|
|
31
|
+
]);
|
|
32
|
+
/** The longest message the quick model takes: longer ones are rarely a plain status question. */
|
|
33
|
+
export const QUICK_MESSAGE_MAX_CHARS = 240;
|
|
34
|
+
const words = (text) => text.toLowerCase().replace(/[’]/g, "'").split(/[^a-z0-9']+/).filter(one => one !== "");
|
|
35
|
+
/** Whether a message reads as a status or read question with no act word (§8's message rule). */
|
|
36
|
+
export function quickQuestion(message) {
|
|
37
|
+
const text = message.trim();
|
|
38
|
+
if (text === "" || text.length > QUICK_MESSAGE_MAX_CHARS || /[\r\n]/.test(text))
|
|
39
|
+
return false;
|
|
40
|
+
const all = words(text);
|
|
41
|
+
if (all.length === 0)
|
|
42
|
+
return false;
|
|
43
|
+
if (all.some(word => QUICK_ACT_WORDS.includes(word.replace(/'s$/, ""))))
|
|
44
|
+
return false;
|
|
45
|
+
// A negated or conditional request ("don't…", "if…, then…") is a judgement call: the main model takes it.
|
|
46
|
+
if (all.some(word => word === "don't" || word === "dont" || word === "if" || word === "please"))
|
|
47
|
+
return false;
|
|
48
|
+
return text.endsWith("?") || ASKING_OPENINGS.has(all[0]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The tools a quick reply's requests advertise: reads only. Every one is a read of saved state as this person sees it;
|
|
52
|
+
* none drafts, records a question, a promise, a memory or a card, or opens a page. `find_tools` is advertised beside them
|
|
53
|
+
* but is not one of them: it is how a quick step says it needs something else ("search before saying something can't
|
|
54
|
+
* be done"), so calling it hands the reply to the main model, unrun (more conservative than §8's "find_tools for
|
|
55
|
+
* something to change": any search escalates).
|
|
56
|
+
*/
|
|
57
|
+
export const QUICK_READ_TOOLS = new Set([
|
|
58
|
+
"get_brief", "get_project_context", "get_code_map", "get_project_activity", "get_actions", "get_action_status", "get_skills",
|
|
59
|
+
"get_acceptance_evidence", "recap", "list_repos", "list_tasks", "get_task", "get_result", "get_result_images", "get_controls",
|
|
60
|
+
"get_agents", "get_project_knowledge", "get_flaky_tests", "get_task_conversation", "get_diff", "get_check_log",
|
|
61
|
+
"get_project_tools", "get_flows", "get_flow_insights", "list_decisions", "get_decision", "queue",
|
|
62
|
+
"get_findings", "get_subagents", "get_person", "get_integrations", "get_capabilities", "search_project_memory", "get_models", "get_sweep",
|
|
63
|
+
]);
|
|
64
|
+
/** Whether a quick step's request advertises this tool: the reads, and find_tools as the way to ask for more. */
|
|
65
|
+
export function quickAdvertises(name) {
|
|
66
|
+
return QUICK_READ_TOOLS.has(name) || name === "find_tools";
|
|
67
|
+
}
|
|
68
|
+
/** The faster model, as the model catalog names it: the newest Sonnet this computer's Claude has been seen to run, else
|
|
69
|
+
* the compiled current one. Always an exact id, never an alias that could move under a recorded route. */
|
|
70
|
+
export function quickLeadModel(store) {
|
|
71
|
+
const seen = aliasTarget(store, "sonnet")?.id ?? null;
|
|
72
|
+
return seen !== null && exactModelId(seen) ? seen : SONNET;
|
|
73
|
+
}
|
|
74
|
+
/** Which model answers: the faster one only when every condition holds. */
|
|
75
|
+
export function quickRoute(input) {
|
|
76
|
+
if (!input.enabled || input.provider !== "claude-subscription" || !input.quickAllowed)
|
|
77
|
+
return { route: "default" };
|
|
78
|
+
if (input.quickModel === input.mainModel || input.hasContext || input.hasReplyImage || input.askOpen)
|
|
79
|
+
return { route: "default" };
|
|
80
|
+
return quickQuestion(input.message) ? { route: "fast", model: input.quickModel } : { route: "default" };
|
|
81
|
+
}
|
|
82
|
+
/** Whether a quick step asked for anything beyond the quick reads: its calls are then discarded unrun (§8). */
|
|
83
|
+
export function quickStepEscalates(calls) {
|
|
84
|
+
return calls.some(call => !QUICK_READ_TOOLS.has(call.name));
|
|
85
|
+
}
|
|
86
|
+
/** "Sonnet 5.5" for claude-sonnet-5-5; the id itself for anything else. */
|
|
87
|
+
export function modelName(model) {
|
|
88
|
+
if (model === "default")
|
|
89
|
+
return "the main model";
|
|
90
|
+
const family = /^claude-(opus|sonnet|haiku|fable)-(\d+)(?:-(\d+))?/.exec(model);
|
|
91
|
+
if (family !== null)
|
|
92
|
+
return `${family[1].charAt(0).toUpperCase()}${family[1].slice(1)} ${family[2]}${family[3] === undefined ? "" : `.${family[3]}`}`;
|
|
93
|
+
if (/^(opus|sonnet|haiku|fable)$/.test(model))
|
|
94
|
+
return `${model.charAt(0).toUpperCase()}${model.slice(1)}`;
|
|
95
|
+
return model;
|
|
96
|
+
}
|
|
97
|
+
/** The activity line's words for a routed reply: "answered by Sonnet 5.5", "moved to Opus"; nothing for the default. */
|
|
98
|
+
export function routeWords(route, quickModel, mainModel) {
|
|
99
|
+
if (route === "fast")
|
|
100
|
+
return `answered by ${modelName(quickModel)}`;
|
|
101
|
+
if (route === "escalated")
|
|
102
|
+
return `moved to ${modelName(mainModel)}`;
|
|
103
|
+
return null;
|
|
104
|
+
}
|
package/dist/lead-status.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type LimitWindow } from "./provider-limits.js";
|
|
2
2
|
import { type RunCheckSuite, type Store } from "./store.js";
|
|
3
|
+
import { type HomeFigures } from "./home-groups.js";
|
|
3
4
|
export type CheckSummary = {
|
|
4
5
|
status: "passed" | "failed" | "not-run" | "unknown";
|
|
5
6
|
exitCode: number | null;
|
|
@@ -84,6 +85,8 @@ export type InstallationStatus = {
|
|
|
84
85
|
task: string | null;
|
|
85
86
|
line: string;
|
|
86
87
|
} | null;
|
|
88
|
+
/** Home's figures from the same work index totals (home-groups.ts): each the count of the Tasks list it names. */
|
|
89
|
+
figures: HomeFigures;
|
|
87
90
|
};
|
|
88
91
|
/** One indexed observation for `task wait`. `watchedRun` is the attempt the
|
|
89
92
|
* caller first saw. Each poll follows the task's newest attempt from there, so
|
package/dist/lead-status.js
CHANGED
|
@@ -5,6 +5,7 @@ import { windowLabel } from "./provider-limits.js";
|
|
|
5
5
|
import { openAuthPauses, signInCommand, signInReason } from "./provider-auth.js";
|
|
6
6
|
import { BUILT_IN, checkSuitesOf } from "./store.js";
|
|
7
7
|
import { workIndexPage, workReadyForReview } from "./work-index.js";
|
|
8
|
+
import { homeFiguresLine, homeFiguresOf } from "./home-groups.js";
|
|
8
9
|
import { currentActor } from "./actor.js";
|
|
9
10
|
import { leadActivity, leadActivityLine } from "./lead-voice.js";
|
|
10
11
|
const maybeNumber = (value) => value === null || value === undefined || !Number.isInteger(Number(value)) ? null : Number(value);
|
|
@@ -234,6 +235,8 @@ export function installationStatus(store, now, viewer = currentActor()?.account
|
|
|
234
235
|
// One per family, from the same projection as the Tasks list: a completed
|
|
235
236
|
// family (root or any revision) and a superseded version never count.
|
|
236
237
|
const ready = workReadyForReview(store, now, access, 5);
|
|
238
|
+
// The most urgent tasks and Home's figures, from one read of the work index.
|
|
239
|
+
const index = workIndexPage(store, now, { ...access, viewer }, { limit: 8, ...(root === undefined ? {} : { root }) });
|
|
237
240
|
const checkRow = (run) => store.handle.prepare(`SELECT ${CHECK_COLUMNS} FROM run_check AS rc WHERE rc.run = ?`).get(run);
|
|
238
241
|
// Run ids grow with time, so the newest finished release check is the
|
|
239
242
|
// first finished row down the partial index.
|
|
@@ -265,8 +268,8 @@ export function installationStatus(store, now, viewer = currentActor()?.account
|
|
|
265
268
|
observedAt: String(row["observed_at"]),
|
|
266
269
|
})),
|
|
267
270
|
signIn: pauses.map(one => ({ provider: one.provider, reason: signInReason(one), command: signInCommand(one), since: one.openedAt })),
|
|
268
|
-
tasks:
|
|
269
|
-
|
|
271
|
+
tasks: index.items.map(one => ({ task: one.rootId, title: one.title, headline: one.status.label, sentence: one.status.detail })),
|
|
272
|
+
figures: homeFiguresOf(index.totals),
|
|
270
273
|
lead: repos !== null ? null : (() => {
|
|
271
274
|
// Only the asker's own lead: another person's lead is never "Your lead".
|
|
272
275
|
const activity = leadActivity(store, viewer);
|
|
@@ -280,6 +283,8 @@ export function renderInstallationStatus(status, limited = false) {
|
|
|
280
283
|
// A paused provider comes first: it is the one thing a person must do.
|
|
281
284
|
for (const one of status.signIn)
|
|
282
285
|
lines.push(`${one.reason} — run \`${one.command}\`. Its tasks wait until then.`);
|
|
286
|
+
// Then Home's figures, the same words and counts as Home.
|
|
287
|
+
lines.push(homeFiguresLine(status.figures));
|
|
283
288
|
if (status.lead != null)
|
|
284
289
|
lines.push(status.lead.line);
|
|
285
290
|
// Each task's one headline, the same words as the console and chat cards.
|
|
@@ -320,7 +325,7 @@ export function renderInstallationStatus(status, limited = false) {
|
|
|
320
325
|
lines.push(`Plan windows: ${provider} — ${detail}`);
|
|
321
326
|
}
|
|
322
327
|
}
|
|
323
|
-
return lines.slice(0,
|
|
328
|
+
return lines.slice(0, 13 + status.signIn.length + (status.lead == null ? 0 : 1) + (status.tasks.length === 0 ? 0 : status.tasks.length + 1));
|
|
324
329
|
}
|
|
325
330
|
export function renderTaskWait(snapshot, outcome = snapshot.outcome) {
|
|
326
331
|
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/lead-tools.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type FeedbackImage } from "./result-review.js";
|
|
2
|
+
import { type FastPreview } from "./fast-start.js";
|
|
2
3
|
import { type CommitmentChannel } from "./lead-commitments.js";
|
|
3
4
|
import { type Integration } from "./integrations.js";
|
|
4
5
|
import type { AuthMode } from "./keys.js";
|
|
@@ -56,6 +57,10 @@ export type LeadToolContext = {
|
|
|
56
57
|
searches: number;
|
|
57
58
|
found: Set<string>;
|
|
58
59
|
};
|
|
60
|
+
/** propose_start only (docs/plans/one-step-chat.md C3): the preview the conversation loop prepared for THIS call, in
|
|
61
|
+
* this turn, after the call's input was validated (startInputOf). Never read from the model's arguments; absent (a
|
|
62
|
+
* direct call outside a turn), nothing was prepared and the person gets an ordinary task card. */
|
|
63
|
+
startPrepared?: FastPreview;
|
|
59
64
|
};
|
|
60
65
|
export type LeadToolResult = {
|
|
61
66
|
ok: true;
|
|
@@ -145,3 +150,18 @@ export declare function isLeadTool(name: string): boolean;
|
|
|
145
150
|
export declare function executeLeadTool(ctx: LeadToolContext, name: string, args: Record<string, unknown>, view?: LeadViewContext): LeadToolResult;
|
|
146
151
|
/** Bytes of a serialized tool result as it will sit inside the request body. */
|
|
147
152
|
export declare function toolResultBytes(text: string): number;
|
|
153
|
+
/** What propose_start may start with, checked before Toolroll asks the classifier (docs/plans/one-step-chat.md C3): the
|
|
154
|
+
* call's contract, the same memory and capability checks every proposal makes, never a team chat, a project the person
|
|
155
|
+
* can reach now, and the same text and secret checks as propose_task. The conversation loop runs this, and only then
|
|
156
|
+
* prepares a preview; the handler runs it again. Invalid input never reaches the classifier. */
|
|
157
|
+
export declare function startInputOf(ctx: LeadToolContext, args: Record<string, unknown>): {
|
|
158
|
+
ok: true;
|
|
159
|
+
input: {
|
|
160
|
+
repo: string;
|
|
161
|
+
repoId: string;
|
|
162
|
+
title: string;
|
|
163
|
+
};
|
|
164
|
+
} | {
|
|
165
|
+
ok: false;
|
|
166
|
+
message: string;
|
|
167
|
+
};
|
package/dist/lead-tools.js
CHANGED
|
@@ -17,6 +17,7 @@ import { taskControlOf } from "./task-control.js";
|
|
|
17
17
|
import { taskWorkSummaryOf } from "./work-summary.js";
|
|
18
18
|
import { assignmentOf, assignmentBrief } from "./assignment.js";
|
|
19
19
|
import { validateScopeText, validateTaskText } from "./task-text.js";
|
|
20
|
+
import { fastRefusalWords } from "./fast-start.js";
|
|
20
21
|
import { conversationKnowledge, knowledgeView } from "./project-knowledge.js";
|
|
21
22
|
import { fixTaskOf, flakyList, flakyShow } from "./flaky.js";
|
|
22
23
|
import { cancelCommitment, CHECK_RESULTS, conditionWords, latestResultRun, recordCommitment, RUN_OUTCOMES, TASK_STATES } from "./lead-commitments.js";
|
|
@@ -393,7 +394,7 @@ export function agentsOver(store, taskId, now) {
|
|
|
393
394
|
export const LEAD_CORE_TOOLS = new Set([
|
|
394
395
|
"find_tools", "get_brief", "recap", "list_repos", "get_project_context", "get_project_activity", "search_project_memory", "get_capabilities",
|
|
395
396
|
"list_tasks", "get_task", "get_result", "get_diff", "get_check_log", "list_decisions", "get_decision",
|
|
396
|
-
"get_actions", "get_action_status", "propose_action", "propose_task", "propose_scope", "propose_steer",
|
|
397
|
+
"get_actions", "get_action_status", "propose_action", "propose_task", "propose_start", "propose_scope", "propose_steer",
|
|
397
398
|
"propose_answer", "propose_review", "remember", "ask_owner", "commit_to",
|
|
398
399
|
// The lead's queue verbs: basic, and small enough that deferring them saves almost nothing.
|
|
399
400
|
"queue", "propose_next", "propose_reserve", "propose_hold", "propose_unhold", "propose_cancel",
|
|
@@ -596,7 +597,7 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
596
597
|
},
|
|
597
598
|
get_actions: {
|
|
598
599
|
description: "List shared actions and required inputs. All channels use the same approvals.",
|
|
599
|
-
handle: () => ({ ok: true, body: { actions: Object.entries(CHAT_ACTIONS).filter(([operation]) => !operation.startsWith('flow_') && !operation.startsWith('subagent_')).map(([operation, value]) => ({ operation, label: value.label, secureReview: value.protected, inputs: CHAT_ACTION_FIELDS[operation].filter(field => field !== 'nonce' && field !== 'files') })), notice: 'Passwords and credentials never belong in a tool call or conversation. Secure review links complete protected actions.' } }),
|
|
600
|
+
handle: () => ({ ok: true, body: { actions: Object.entries(CHAT_ACTIONS).filter(([operation]) => !operation.startsWith('flow_') && !operation.startsWith('subagent_') && operation !== 'task_start').map(([operation, value]) => ({ operation, label: value.label, secureReview: value.protected, inputs: CHAT_ACTION_FIELDS[operation].filter(field => field !== 'nonce' && field !== 'files') })), notice: 'Passwords and credentials never belong in a tool call or conversation. Secure review links complete protected actions.' } }),
|
|
600
601
|
},
|
|
601
602
|
propose_action: {
|
|
602
603
|
description: "Read get_actions and relevant skills/evidence first. Save an exact-state proposal only; protected or long terms require full secure review.",
|
|
@@ -608,6 +609,9 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
608
609
|
return { ok: false, message: 'Use propose_flow for flows.' };
|
|
609
610
|
if (operation.startsWith('subagent_'))
|
|
610
611
|
return { ok: false, message: 'Use propose_subagent for subagents.' };
|
|
612
|
+
// A Start card is drafted only by propose_start, whose preview comes from the turn loop, never from arguments.
|
|
613
|
+
if (operation === 'task_start')
|
|
614
|
+
return { ok: false, message: 'Use propose_start for a small change to start now.' };
|
|
611
615
|
const input = { ...args };
|
|
612
616
|
delete input['operation'];
|
|
613
617
|
if (operation.startsWith('skill_') || operation.startsWith('knowledge_') || operation.startsWith('tool_') || operation === 'mode_revoke' || operation === 'memory_decide') {
|
|
@@ -1709,7 +1713,7 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
1709
1713
|
},
|
|
1710
1714
|
},
|
|
1711
1715
|
search_project_memory: {
|
|
1712
|
-
description: "Search decisions, instructions, references, lessons and the conversations you may read, across your projects. Read-only;
|
|
1716
|
+
description: "Search decisions, instructions, references, lessons and the conversations you may read, across your projects. Read-only; name what you rely on by what it settled ('you chose Postgres in March'); give its kind and id when asked.",
|
|
1713
1717
|
handle: (ctx, args) => {
|
|
1714
1718
|
const repo = args['repo'] === undefined ? null : repoPathOf(ctx.who, args['repo']);
|
|
1715
1719
|
if (args['repo'] !== undefined && repo === null)
|
|
@@ -1925,6 +1929,35 @@ const LEAD_TOOL_HANDLERS = {
|
|
|
1925
1929
|
return { ok: true, body: { proposal: id, kind: "task", repo: args["repo"], deliverable: report ? "report" : "branch", planning: report ? "not needed for a scout" : planning, awaiting: "the operator's confirmation" } };
|
|
1926
1930
|
},
|
|
1927
1931
|
},
|
|
1932
|
+
propose_start: {
|
|
1933
|
+
description: "The person asked for one small, clear change to be done now. Toolroll decides whether it can start right away; if not, you get an ordinary task card. title is the change in their words, one line.",
|
|
1934
|
+
handle: (ctx, args) => {
|
|
1935
|
+
const checked = startInputOf(ctx, args);
|
|
1936
|
+
if (!checked.ok)
|
|
1937
|
+
return { ok: false, message: checked.message };
|
|
1938
|
+
const { repo, repoId, title } = checked.input;
|
|
1939
|
+
// Eligible: a Start card from the loop's own preview (the request names it back; the card checks it is this
|
|
1940
|
+
// person's, this turn's, on chat). Anything else: today's task card, built directly, and why in the body.
|
|
1941
|
+
const prepared = ctx.startPrepared;
|
|
1942
|
+
let why = fastRefusalWords(prepared === undefined ? "classifier" : prepared.eligible ? "classifier" : prepared.reason);
|
|
1943
|
+
if (prepared !== undefined && prepared.eligible) {
|
|
1944
|
+
try {
|
|
1945
|
+
const action = prepareSharedAction(ctx.store, ctx.who, "task_start", { repo, preview: prepared.preview }, ctx.evidenceRoot, ctx.now, ctx.turn);
|
|
1946
|
+
const id = ctx.draft("action", { ...action });
|
|
1947
|
+
if (id === null)
|
|
1948
|
+
return tooMany();
|
|
1949
|
+
return { ok: true, body: { proposal: id, kind: "action", label: action.title, startsNow: true, awaiting: "the person's own yes on Start work", executed: false } };
|
|
1950
|
+
}
|
|
1951
|
+
catch (error) {
|
|
1952
|
+
why = error instanceof Error ? error.message : why;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
const id = ctx.draft("task", { repo, repoId, title, goal: title, not: null, touches: [], acceptance: [{ id: "c1", statement: title, evidence: ["manual-review"] }], planning: "skip", report: false });
|
|
1956
|
+
if (id === null)
|
|
1957
|
+
return tooMany();
|
|
1958
|
+
return { ok: true, body: { proposal: id, kind: "task", repo: repoId, startsNow: false, why, awaiting: "the operator's confirmation" } };
|
|
1959
|
+
},
|
|
1960
|
+
},
|
|
1928
1961
|
propose_sweep: {
|
|
1929
1962
|
description: "Several independent items under ONE shared rule set (a bug sweep, a batch of small fixes): 2-20 items, each becomes its own task in its own worktree, started with one approval, at most max_parallel (default 3) at once, each reporting a verdict into one table. brief holds the shared rules every item gets (e.g. reproduce first; 'not reproducible' is a valid result; prove the fix); each item is only its own report. Not for one task, and not for steps that depend on each other.",
|
|
1930
1963
|
handle: (ctx, args) => {
|
|
@@ -2515,3 +2548,33 @@ export function executeLeadTool(ctx, name, args, view) {
|
|
|
2515
2548
|
export function toolResultBytes(text) {
|
|
2516
2549
|
return Buffer.byteLength(JSON.stringify(text), "utf8");
|
|
2517
2550
|
}
|
|
2551
|
+
/** What propose_start may start with, checked before Toolroll asks the classifier (docs/plans/one-step-chat.md C3): the
|
|
2552
|
+
* call's contract, the same memory and capability checks every proposal makes, never a team chat, a project the person
|
|
2553
|
+
* can reach now, and the same text and secret checks as propose_task. The conversation loop runs this, and only then
|
|
2554
|
+
* prepares a preview; the handler runs it again. Invalid input never reaches the classifier. */
|
|
2555
|
+
export function startInputOf(ctx, args) {
|
|
2556
|
+
const tool = LEAD_TOOLS.find(one => one.name === "propose_start");
|
|
2557
|
+
if (tool === undefined)
|
|
2558
|
+
return { ok: false, message: "propose_start isn't available." };
|
|
2559
|
+
const read = tool.read(ctx, args);
|
|
2560
|
+
if (!read.ok)
|
|
2561
|
+
return { ok: false, message: read.issues.map(one => one.line).join("\n") };
|
|
2562
|
+
const unchecked = memoryUnsearched(ctx, "propose_start", read.value) ?? capabilitiesUnread(ctx, "propose_start", read.value);
|
|
2563
|
+
if (unchecked !== null && !unchecked.ok)
|
|
2564
|
+
return { ok: false, message: unchecked.message };
|
|
2565
|
+
// A start is the person's own yes in their own chat with you: a shared team chat proposes an ordinary task.
|
|
2566
|
+
if (teamRoomOf(ctx.store, ctx.thread) !== null)
|
|
2567
|
+
return { ok: false, message: "Starting work in one step is for the person's own chat. Use propose_task here." };
|
|
2568
|
+
const repo = repoPathOf(ctx.who, read.value["repo"]);
|
|
2569
|
+
if (repo === null || !ctx.store.accountCanAccess(ctx.who.name, repo))
|
|
2570
|
+
return { ok: false, message: "repo must be one of the ids from list_repos" };
|
|
2571
|
+
const title = read.value["title"];
|
|
2572
|
+
if (typeof title !== "string")
|
|
2573
|
+
return { ok: false, message: "title is the change, one line" };
|
|
2574
|
+
const badText = validateTaskText({ title, goal: title, outOfScope: null });
|
|
2575
|
+
if (badText !== null)
|
|
2576
|
+
return { ok: false, message: badText.message };
|
|
2577
|
+
if (scanForSecrets(title).length > 0)
|
|
2578
|
+
return { ok: false, message: "Task text cannot contain credentials." };
|
|
2579
|
+
return { ok: true, input: { repo, repoId: String(read.value["repo"]), title } };
|
|
2580
|
+
}
|
package/dist/lead-voice.js
CHANGED
|
@@ -11,7 +11,7 @@ import { BATCH_MS, chatText, chatTitle } from "./chat-voice.js";
|
|
|
11
11
|
import { BUILT_IN, refArchived } from "./store.js";
|
|
12
12
|
import { leadNameOf } from "./lead-identity.js";
|
|
13
13
|
import { leadLapsed } from "./task-status.js";
|
|
14
|
-
import {
|
|
14
|
+
import { publicChatText as plain } from "./chat-display.js";
|
|
15
15
|
export const LEAD_SAY_KIND = "lead-say";
|
|
16
16
|
/** Repaints the owner's task card when the lead takes a task on: it never makes a new message in quiet chat. */
|
|
17
17
|
export const LEAD_ON_IT_KIND = "lead-on-it";
|
package/dist/lead.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type LeadProgress } from "./lead-progress.js";
|
|
2
2
|
import type { ChatConfig, LeadSession, LeadThread, Store } from "./store.js";
|
|
3
|
+
import type { LeadFailureDetail } from "./lead-latency-schema.js";
|
|
3
4
|
import type { Integration } from "./integrations.js";
|
|
4
5
|
import type { VerifiedApprover } from "./principal.js";
|
|
5
6
|
import { type LeadHistoryMessage } from "./converse.js";
|
|
@@ -128,8 +129,9 @@ export declare const LEAD_FAILURE_COPY: {
|
|
|
128
129
|
readonly empty: "The lead sent back an empty answer. Nothing was kept. Send your message again.";
|
|
129
130
|
readonly unusable: "The lead's answer couldn't be used, so it was thrown away. Nothing was kept. Send your message again.";
|
|
130
131
|
};
|
|
131
|
-
/** A failed turn's saved reason as plain words, for a surface that reads the turn back later (a restart, a delivery retry).
|
|
132
|
-
|
|
132
|
+
/** A failed turn's saved reason as plain words, for a surface that reads the turn back later (a restart, a delivery retry).
|
|
133
|
+
* Its saved detail (v158), when known, names the exact cause the person saw live ("not installed", "sign-in expired"). */
|
|
134
|
+
export declare function leadFailureText(reason: string | null, unknownSpend?: boolean, detail?: LeadFailureDetail | null): string;
|
|
133
135
|
/** How long a provider that ignored its abort at the turn's deadline is waited for before the turn ends without it.
|
|
134
136
|
* Its process group was already killed at the deadline; this only bounds the wait for the transport to say so. */
|
|
135
137
|
export declare const LEAD_ABORT_GRACE_MS = 5000;
|