toolroll 0.9.55 → 0.9.57
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/README.md +24 -8
- package/dist/action-ledger.d.ts +2 -4
- package/dist/action-ledger.js +2 -10
- package/dist/approval-policy.d.ts +5 -5
- package/dist/approval-policy.js +4 -4
- package/dist/approval-rules-ui.js +2 -2
- package/dist/assignment-adapters.d.ts +5 -0
- package/dist/assignment.d.ts +24 -3
- package/dist/assignment.js +36 -10
- package/dist/browser/workspace.js +5 -5
- package/dist/browser-workspace.d.ts +12 -12
- package/dist/browser-workspace.js +1 -1
- package/dist/budget-alerts.js +3 -3
- package/dist/chat-actions.d.ts +23 -18
- package/dist/chat-actions.js +110 -88
- package/dist/chat-ask.d.ts +2 -2
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.d.ts +15 -15
- package/dist/chat-channel.js +20 -20
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.js +1 -1
- package/dist/chat-delivery-state.js +2 -2
- package/dist/chat-delivery.d.ts +2 -2
- package/dist/chat-delivery.js +30 -37
- package/dist/chat-flow.js +3 -3
- package/dist/chat-messages.d.ts +1 -1
- package/dist/chat-messages.js +4 -4
- package/dist/chat-migration.d.ts +1 -1
- package/dist/chat-migration.js +11 -11
- package/dist/chat-rooms.js +3 -3
- package/dist/chat-voice.js +1 -1
- package/dist/chat-warmth.d.ts +2 -2
- package/dist/check-levels.d.ts +3 -0
- package/dist/check-levels.js +5 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +6 -7
- package/dist/contracts/chat-actions.d.ts +32 -27
- package/dist/contracts/chat-actions.js +29 -11
- package/dist/contracts/chat-content.d.ts +1 -1
- package/dist/contracts/chat-content.js +1 -1
- package/dist/contracts/chat-tables.js +4 -4
- package/dist/contracts/console-api.d.ts +6 -6
- package/dist/contracts/console-api.js +7 -7
- package/dist/contracts/flow-propose.d.ts +3 -3
- package/dist/contracts/flow.d.ts +132 -125
- package/dist/contracts/flow.js +27 -12
- package/dist/contracts/lead-context.d.ts +1 -1
- package/dist/contracts/lead-context.js +2 -2
- package/dist/contracts/lead-tools.d.ts +20 -18
- package/dist/contracts/lead-tools.js +15 -15
- package/dist/contracts/stage-output.d.ts +1 -1
- package/dist/contracts/stage-output.js +1 -1
- package/dist/contracts/store-json.d.ts +23 -22
- package/dist/contracts/store-json.js +17 -17
- package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
- package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
- package/dist/converse.d.ts +23 -23
- package/dist/converse.js +28 -28
- package/dist/coordinator-proposals.d.ts +2 -2
- package/dist/coordinator-proposals.js +1 -1
- package/dist/coordinator.js +1 -1
- package/dist/deprecations.d.ts +12 -0
- package/dist/deprecations.js +20 -0
- package/dist/desktop-update-gate.js +8 -4
- package/dist/desktop-update.js +6 -1
- package/dist/discord-chat.js +6 -6
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/dispose.js +0 -5
- package/dist/envelope.js +0 -1
- package/dist/export-ui.js +1 -1
- package/dist/export.js +7 -7
- package/dist/flow-engine.d.ts +2 -2
- package/dist/flow-engine.js +21 -21
- package/dist/flow-insights.d.ts +1 -1
- package/dist/flow-share.js +13 -8
- package/dist/flow-steps.d.ts +5 -5
- package/dist/flow-steps.js +19 -19
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/flows-cli.js +1 -1
- package/dist/flows-ui.js +20 -20
- package/dist/flows.js +19 -19
- package/dist/guides.js +4 -3
- package/dist/invoke.js +1 -1
- package/dist/kits-ui.js +5 -5
- package/dist/kits.d.ts +12 -12
- package/dist/kits.js +31 -31
- package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
- package/dist/{mate-cli.js → lead-cli.js} +38 -38
- package/dist/lead-commitments.js +4 -4
- package/dist/lead-context.d.ts +2 -2
- package/dist/lead-context.js +9 -9
- package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
- package/dist/{mate-contract.js → lead-contract.js} +7 -6
- package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
- package/dist/{mate-doors.js → lead-doors.js} +24 -24
- package/dist/lead-follow.d.ts +5 -5
- package/dist/lead-follow.js +9 -9
- package/dist/lead-people.d.ts +5 -5
- package/dist/lead-people.js +14 -14
- package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
- package/dist/{mate-progress.js → lead-progress.js} +3 -3
- package/dist/lead-status.d.ts +1 -1
- package/dist/lead-status.js +2 -2
- package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
- package/dist/{mate-tools.js → lead-tools.js} +109 -101
- package/dist/{mate.d.ts → lead.d.ts} +27 -27
- package/dist/{mate.js → lead.js} +102 -102
- package/dist/limits-ui.d.ts +1 -1
- package/dist/limits-ui.js +1 -1
- package/dist/mcp-connect.d.ts +1 -1
- package/dist/mcp-connect.js +3 -3
- package/dist/mcp-core.js +1 -1
- package/dist/memory-pass.d.ts +2 -2
- package/dist/memory-pass.js +7 -7
- package/dist/notification-rows.d.ts +2 -2
- package/dist/operate.d.ts +3 -3
- package/dist/operate.js +52 -42
- package/dist/policy.d.ts +1 -1
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +2 -2
- package/dist/project-delete-ui.js +1 -1
- package/dist/project-delete.d.ts +1 -1
- package/dist/project-delete.js +26 -26
- package/dist/project-memory.js +1 -1
- package/dist/project-tools.d.ts +1 -1
- package/dist/project-tools.js +1 -1
- package/dist/proof.d.ts +9 -0
- package/dist/proof.js +12 -0
- package/dist/recipe-ui.js +1 -1
- package/dist/release-coverage.d.ts +16 -0
- package/dist/release-coverage.js +92 -0
- package/dist/retention.js +4 -4
- package/dist/scope.d.ts +2 -2
- package/dist/scout-report.d.ts +1 -1
- package/dist/scout-report.js +1 -1
- package/dist/serve.js +40 -45
- package/dist/server/chat.d.ts +4 -4
- package/dist/server/chat.js +105 -103
- package/dist/server/chrome.js +3 -3
- package/dist/server/flows.js +69 -62
- package/dist/server/guards.js +3 -3
- package/dist/server/http.d.ts +3 -3
- package/dist/server/http.js +1 -1
- package/dist/server/live.js +6 -6
- package/dist/server/people-tokens.js +1 -1
- package/dist/server/remote.js +12 -10
- package/dist/server/render-chat.d.ts +30 -30
- package/dist/server/render-chat.js +33 -33
- package/dist/server/render-pages.js +2 -2
- package/dist/server/render-results.d.ts +4 -1
- package/dist/server/render-results.js +23 -9
- package/dist/server/render-settings.d.ts +7 -0
- package/dist/server/render-settings.js +3 -0
- package/dist/server/route-table.js +9 -7
- package/dist/server/runtime.d.ts +13 -13
- package/dist/server/settings.js +14 -13
- package/dist/server/tasks.js +34 -30
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.d.ts +2 -2
- package/dist/slack-chat.js +6 -6
- package/dist/spend-ui.d.ts +3 -3
- package/dist/spend-ui.js +9 -9
- package/dist/spend.d.ts +14 -14
- package/dist/spend.js +31 -31
- package/dist/store-baseline.d.ts +19 -0
- package/dist/store-baseline.js +2873 -0
- package/dist/store.d.ts +200 -254
- package/dist/store.js +830 -3152
- package/dist/subagent-admin.d.ts +53 -0
- package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
- package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
- package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
- package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
- package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
- package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
- package/dist/{teammate-question.js → subagent-question.js} +17 -17
- package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
- package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
- package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
- package/dist/{teammate-week.js → subagent-week.js} +23 -23
- package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
- package/dist/{teammate-work.js → subagent-work.js} +50 -50
- package/dist/subagents-ui.d.ts +22 -0
- package/dist/{teammates-ui.js → subagents-ui.js} +45 -45
- package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
- package/dist/{teammates.js → subagents.js} +21 -21
- package/dist/subscription-chat.d.ts +13 -13
- package/dist/subscription-chat.js +19 -19
- package/dist/surface.d.ts +19 -2
- package/dist/surface.js +7 -31
- package/dist/task-outcome-cli.js +3 -1
- package/dist/task-status.d.ts +59 -7
- package/dist/task-status.js +132 -16
- package/dist/team-cli.d.ts +1 -0
- package/dist/team-cli.js +14 -2
- package/dist/team-leads.js +13 -13
- package/dist/team-runtime.d.ts +2 -2
- package/dist/team-runtime.js +9 -9
- package/dist/teams-chat.js +6 -6
- package/dist/telegram-flow.d.ts +3 -3
- package/dist/telegram-flow.js +3 -3
- package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
- package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
- package/dist/telegram-status.d.ts +11 -4
- package/dist/telegram-status.js +18 -11
- package/dist/telegram-team.d.ts +1 -1
- package/dist/telegram-team.js +5 -5
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +13 -34
- package/dist/text-limits.d.ts +18 -18
- package/dist/text-limits.js +18 -18
- package/dist/toolroll-update.d.ts +8 -0
- package/dist/toolroll-update.js +45 -8
- package/dist/work-index.d.ts +6 -1
- package/dist/work-index.js +50 -8
- package/dist/workspace-ui.js +2 -1
- package/package.json +1 -2
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -21
package/dist/guides.js
CHANGED
|
@@ -318,7 +318,7 @@ ${AUTHORITY_LINE}
|
|
|
318
318
|
transcripts following.
|
|
319
319
|
- **projects** (\`/projects\`): enrol, open, or switch repositories; the
|
|
320
320
|
project name in the header is the one-tap switcher on every screen.
|
|
321
|
-
- **chat** (\`/chat\`): the
|
|
321
|
+
- **chat** (\`/chat\`): the lead — a conversation across every project
|
|
322
322
|
that only PROPOSES (file, reorder, reserve, hold, rewrite a scope,
|
|
323
323
|
cancel, answer a decision); each proposal is a card the person
|
|
324
324
|
confirms. Coordinators over the MCP gateway propose the same way, and
|
|
@@ -340,7 +340,7 @@ ${AUTHORITY_LINE}
|
|
|
340
340
|
- \`toolroll peek\`: one pane per live agent (digits focus, \`q\`
|
|
341
341
|
leaves); \`peek <run>\` follows one; \`peek --tmux\` opens a tmux window
|
|
342
342
|
per run.
|
|
343
|
-
- \`toolroll chat\`: the
|
|
343
|
+
- \`toolroll chat\`: the lead in the terminal (\`--say "…"\` for one
|
|
344
344
|
turn); \`toolroll proposals\` lists and confirms coordinator
|
|
345
345
|
proposals.
|
|
346
346
|
- \`toolroll task add "<title>" --report\`: a SCOUT task — a
|
|
@@ -359,7 +359,8 @@ needs you · queued · waiting · building · done · failed; a *scope* is the
|
|
|
359
359
|
contract a person approves; a *decision* is a typed question an agent
|
|
360
360
|
parked; a *hold* pauses the next attempt; a *stale approval* means the
|
|
361
361
|
approval no longer matches how builds are routed — approving again fixes
|
|
362
|
-
it; a *scout* delivers a report; the *
|
|
362
|
+
it; a *scout* delivers a report; the *lead* proposes and never acts, and
|
|
363
|
+
its *subagents* work within the rules you write for them.
|
|
363
364
|
`,
|
|
364
365
|
};
|
|
365
366
|
const knowledge = {
|
package/dist/invoke.js
CHANGED
|
@@ -353,7 +353,7 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
353
353
|
const evidence = spec.provider === "claude" ? claudeBillingFrom(billingSeen)
|
|
354
354
|
// Codex signed in with ChatGPT is its plan, unless its plan is itself billed by use (seen by the console's probe).
|
|
355
355
|
: codexLogin === "subscription" ? seenBilling(store.handle, "codex") ?? "subscription" : codexLogin;
|
|
356
|
-
// What this computer's Claude does with no key from us (
|
|
356
|
+
// What this computer's Claude does with no key from us (subagents and drafts bill the same way): learnt only from a
|
|
357
357
|
// run we gave no key that got as far as an answer.
|
|
358
358
|
if (spec.provider === "claude" && managedKey === null && authMode === "subscription" && billingSeen.answered && evidence !== null) {
|
|
359
359
|
store.recordProviderBilling("claude", evidence, new Date());
|
package/dist/kits-ui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { kitChecklist, kitInstalled, KITS } from "./kits.js";
|
|
2
|
-
import {
|
|
2
|
+
import { SUBAGENT_TEMPLATES } from "./subagents.js";
|
|
3
3
|
import { html, postForm } from "./html.js";
|
|
4
4
|
const CHECK = html `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
|
|
5
5
|
const RING = html `<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>`;
|
|
@@ -12,21 +12,21 @@ export const KITS_CSS = `.kits{max-width:960px;min-width:0}.kits-lede{color:var(
|
|
|
12
12
|
`.kit-steps .kit-step-said{flex:1;min-width:0}.kit-steps li[data-done="true"] .kit-step-said{color:var(--so-muted)}.kit-steps form{margin:0}.kit-steps button,.kit-steps .kit-step-go{min-height:40px;white-space:nowrap}` +
|
|
13
13
|
`.kit-steps .kit-step-go{display:inline-flex;align-items:center;padding:0 12px;border:1px solid var(--so-line);border-radius:8px;text-decoration:none;font-size:.9rem}` +
|
|
14
14
|
`@media(max-width:600px){.kit-steps li{flex-wrap:wrap}.kit-steps .kit-step-said{flex-basis:calc(100% - 40px)}.kit form select{font-size:16px}}`;
|
|
15
|
-
const
|
|
15
|
+
const subagentWords = (kit) => { const template = SUBAGENT_TEMPLATES.find(one => one.id === kit.subagent.template); return template === undefined ? kit.subagent.handle : template.label.toLowerCase(); };
|
|
16
16
|
/** The gallery: every kit, set up in a project with one button (or opened, when it's already there). */
|
|
17
17
|
export function kitsGalleryHtml(store, projects, projectName, canSetUp, notice) {
|
|
18
18
|
const cards = KITS.map(kit => {
|
|
19
19
|
const here = projects.filter(repo => kitInstalled(store, kit, repo) !== null);
|
|
20
20
|
const open = projects.length === 1 && here.length === 1;
|
|
21
|
-
const name = kit.
|
|
22
|
-
const gets = `${name} (${
|
|
21
|
+
const name = kit.subagent.handle.charAt(0).toUpperCase() + kit.subagent.handle.slice(1);
|
|
22
|
+
const gets = `${name} (${subagentWords(kit)}) · the ${kit.flowName} flow${kit.tools.length === 0 ? "" : ` · works with ${kit.tools.map(one => one.label).join(" and ")}`}`;
|
|
23
23
|
const action = open ? html `<a class="kit-open" href="/kits/${kit.id}?repo=${encodeURIComponent(projects[0])}">Open ${kit.name} →</a>`
|
|
24
24
|
: !canSetUp || projects.length === 0 ? ""
|
|
25
25
|
: postForm(`/kits/${kit.id}/setup`, html `${projects.length === 1 ? ""
|
|
26
26
|
: html `<label>Project<select name="repo">${projects.map(repo => html `<option value="${repo}">${projectName(repo)}${here.includes(repo) ? " (set up)" : ""}</option>`)}</select></label>`}<button>Set it up</button>`, { hidden: { repo: projects.length === 1 ? projects[0] : null } });
|
|
27
27
|
return html `<article class="kit" data-kit="${kit.id}"><h2>${kit.name}</h2><p class="kit-promise">${kit.promise}</p><p class="kit-gets">${gets}</p>${action}</article>`;
|
|
28
28
|
});
|
|
29
|
-
return html `<section class="kits">${notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : ""}<p class="kits-lede">A working setup in one click:
|
|
29
|
+
return html `<section class="kits">${notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : ""}<p class="kits-lede">A working setup in one click: a subagent, the flow it works, and what to connect next. Nothing goes out without you approving it.</p><div class="kit-grid">${cards}</div></section>`;
|
|
30
30
|
}
|
|
31
31
|
/** A kit's own page: what's set up, and the next step on each line. */
|
|
32
32
|
export function kitPageHtml(store, kit, repo, dir, canSetUp, notice) {
|
package/dist/kits.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FlowStepInput } from "./flows.js";
|
|
2
|
-
import type { FlowRow, Store,
|
|
3
|
-
import { type ToolIo } from "./
|
|
4
|
-
/** A tool a kit's
|
|
2
|
+
import type { FlowRow, Store, SubagentRow } from "./store.js";
|
|
3
|
+
import { type ToolIo } from "./subagent-tools.js";
|
|
4
|
+
/** A tool a kit's subagent works better with: the catalog entry that connects it, and why. */
|
|
5
5
|
export type KitTool = {
|
|
6
6
|
tool: string;
|
|
7
7
|
label: string;
|
|
@@ -11,8 +11,8 @@ export type Kit = {
|
|
|
11
11
|
id: string;
|
|
12
12
|
name: string;
|
|
13
13
|
promise: string;
|
|
14
|
-
/** The
|
|
15
|
-
|
|
14
|
+
/** The subagent it hires: a template, and the name it starts with. */
|
|
15
|
+
subagent: {
|
|
16
16
|
template: string;
|
|
17
17
|
handle: string;
|
|
18
18
|
};
|
|
@@ -38,23 +38,23 @@ type Done = {
|
|
|
38
38
|
ok: true;
|
|
39
39
|
said: string;
|
|
40
40
|
flow: number;
|
|
41
|
-
|
|
41
|
+
subagent: number;
|
|
42
42
|
} | {
|
|
43
43
|
ok: false;
|
|
44
44
|
said: string;
|
|
45
45
|
};
|
|
46
|
-
/** A kit set up in a project: its flow (by name) and its
|
|
46
|
+
/** A kit set up in a project: its flow (by name) and its subagent, when both are there. */
|
|
47
47
|
export declare function kitInstalled(store: Store, kit: Kit, repo: string): {
|
|
48
48
|
flow: FlowRow;
|
|
49
|
-
mate:
|
|
49
|
+
mate: SubagentRow;
|
|
50
50
|
} | null;
|
|
51
51
|
/**
|
|
52
|
-
* Set a kit up: its
|
|
53
|
-
* flow, and its button triggers; then its
|
|
52
|
+
* Set a kit up: its subagent (kept if one of that name is already there), its
|
|
53
|
+
* flow, and its button triggers; then its subagent may use whichever of the
|
|
54
54
|
* kit's tools the project already has (reading free, writing asking first).
|
|
55
55
|
*/
|
|
56
56
|
export declare function setUpKit(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, io?: ToolIo): Promise<Done>;
|
|
57
|
-
/** The same, all at once (a chat confirmation can't wait on a tool): its
|
|
57
|
+
/** The same, all at once (a chat confirmation can't wait on a tool): its subagent is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
|
|
58
58
|
export declare function setUpKitNow(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, grantByName?: boolean): Done;
|
|
59
59
|
/** One line of a kit's checklist: done, or the next step with where to take it. */
|
|
60
60
|
export type KitStep = {
|
|
@@ -67,7 +67,7 @@ export type KitStep = {
|
|
|
67
67
|
};
|
|
68
68
|
/** What's set up and what's left, read fresh each time. */
|
|
69
69
|
export declare function kitChecklist(store: Store, kit: Kit, repo: string, dir: string | null): KitStep[];
|
|
70
|
-
/** A sample card in the kit's first zone, so its
|
|
70
|
+
/** A sample card in the kit's first zone, so its subagent starts on it at once. */
|
|
71
71
|
export declare function addKitSample(store: Store, kit: Kit, repo: string, by: string, now: Date): {
|
|
72
72
|
ok: true;
|
|
73
73
|
href: string;
|
package/dist/kits.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Starter kits: a working setup in one click —
|
|
2
|
+
* Starter kits: a working setup in one click — a subagent, the flow
|
|
3
3
|
* it works, and the triggers that are safe to add straight away — plus a
|
|
4
4
|
* checklist of what's left (connect email, connect a tool) and a sample card
|
|
5
|
-
* that shows the
|
|
5
|
+
* that shows the subagent at work within seconds.
|
|
6
6
|
*
|
|
7
7
|
* A kit adds nothing a person couldn't add by hand, and nothing that acts
|
|
8
8
|
* outside: replies go out only after you approve them, a code change is filed
|
|
@@ -16,17 +16,17 @@ import { googleConnected } from "./google-mail.js";
|
|
|
16
16
|
import { readEmailSettings } from "./email-settings.js";
|
|
17
17
|
import { mailboxReady } from "./mailbox.js";
|
|
18
18
|
import { projectToolsOf } from "./project-tools.js";
|
|
19
|
-
import {
|
|
20
|
-
import { grantListed, grantTool } from "./
|
|
21
|
-
import {
|
|
19
|
+
import { createSubagentFrom, nameOf } from "./subagent-admin.js";
|
|
20
|
+
import { grantListed, grantTool } from "./subagent-tools.js";
|
|
21
|
+
import { SUBAGENT_TEMPLATES } from "./subagents.js";
|
|
22
22
|
export const KITS = [
|
|
23
23
|
{
|
|
24
24
|
id: "support-desk", name: "Support desk",
|
|
25
25
|
promise: "Maya answers customer emails, looks orders up and refunds within limits you set. You approve each reply before it goes out.",
|
|
26
|
-
|
|
26
|
+
subagent: { template: "support", handle: "maya" },
|
|
27
27
|
flowName: "Support desk",
|
|
28
28
|
steps: [
|
|
29
|
-
{ id: "answer", title: "Maya answers", kind: "
|
|
29
|
+
{ id: "answer", title: "Maya answers", kind: "subagent", subagent: "maya",
|
|
30
30
|
instructions: "Read the customer's message. Look things up (and refund, within your rules) with your tools when it helps. Write the reply we'd send in \"text\". Pick Reply when it's ready for the customer, or Needs a person when your rules say to ask first or you can't help.",
|
|
31
31
|
routes: [{ answer: "Reply", goesTo: "You check the reply" }, { answer: "Needs a person", goesTo: "For you" }], ifFails: "For you" },
|
|
32
32
|
{ id: "check", title: "You check the reply", kind: "approval", decider: "owner", next: "Send it", ifFails: "Maya answers" },
|
|
@@ -44,10 +44,10 @@ export const KITS = [
|
|
|
44
44
|
{
|
|
45
45
|
id: "bug-triage", name: "Bug triage",
|
|
46
46
|
promise: "Theo reads every new issue, says what it is and how urgent, answers questions, and turns real bugs into fixes you approve.",
|
|
47
|
-
|
|
47
|
+
subagent: { template: "triage", handle: "theo" },
|
|
48
48
|
flowName: "Bug triage",
|
|
49
49
|
steps: [
|
|
50
|
-
{ id: "sort", title: "Theo sorts it", kind: "
|
|
50
|
+
{ id: "sort", title: "Theo sorts it", kind: "subagent", subagent: "theo",
|
|
51
51
|
instructions: "Read the report. In \"text\", say in one or two sentences what it is and how urgent (for a bug, add what you'd check first; for a question, write the answer). Pick Bug for something broken in this project's code, Question for someone asking how something works, or Duplicate or noise for anything else, saying why.",
|
|
52
52
|
routes: [{ answer: "Bug", goesTo: "Fix it" }, { answer: "Question", goesTo: "Answered" }, { answer: "Duplicate or noise", goesTo: "Closed" }], ifFails: "For you" },
|
|
53
53
|
{ id: "fix", title: "Fix it", kind: "task", planning: "auto", instructions: "Fix the bug this card reports. Theo's notes on it: {{stage.sort}}", next: "You review the fix" },
|
|
@@ -67,10 +67,10 @@ export const KITS = [
|
|
|
67
67
|
{
|
|
68
68
|
id: "sales-follow-up", name: "Sales follow-up",
|
|
69
69
|
promise: "Leo writes back to every new lead within minutes, you approve the email, and it waits for their answer, so no lead goes cold.",
|
|
70
|
-
|
|
70
|
+
subagent: { template: "sales", handle: "leo" },
|
|
71
71
|
flowName: "Sales follow-up",
|
|
72
72
|
steps: [
|
|
73
|
-
{ id: "write", title: "Leo writes back", kind: "
|
|
73
|
+
{ id: "write", title: "Leo writes back", kind: "subagent", subagent: "leo",
|
|
74
74
|
instructions: "Read the lead. In \"text\", write a short, friendly first reply that answers what they asked and suggests one clear next step. Pick Write back, or Not a fit (say why).",
|
|
75
75
|
routes: [{ answer: "Write back", goesTo: "You check it" }, { answer: "Not a fit", goesTo: "Not a fit" }], ifFails: "Not a fit" },
|
|
76
76
|
{ id: "check", title: "You check it", kind: "approval", decider: "owner", next: "Send it", ifFails: "Leo writes back" },
|
|
@@ -87,10 +87,10 @@ export const KITS = [
|
|
|
87
87
|
{
|
|
88
88
|
id: "ops-requests", name: "Ops requests",
|
|
89
89
|
promise: "Ada approves routine requests under $200 without you and brings you everything else, so the team gets answers the same day.",
|
|
90
|
-
|
|
90
|
+
subagent: { template: "ops", handle: "ada" },
|
|
91
91
|
flowName: "Ops requests",
|
|
92
92
|
steps: [
|
|
93
|
-
{ id: "decide", title: "Ada decides", kind: "approval",
|
|
93
|
+
{ id: "decide", title: "Ada decides", kind: "approval", subagent: "ada", decider: "owner", next: "Tell the team", ifFails: "Needs more" },
|
|
94
94
|
{ id: "tell", title: "Tell the team", kind: "notify", message: "Approved: {{card.title}}", next: "Done" },
|
|
95
95
|
{ id: "more", title: "Needs more", kind: "inbox" },
|
|
96
96
|
{ id: "done", title: "Done", kind: "done" },
|
|
@@ -104,22 +104,22 @@ export const KITS = [
|
|
|
104
104
|
},
|
|
105
105
|
];
|
|
106
106
|
export const kitOf = (id) => KITS.find(one => one.id === id) ?? null;
|
|
107
|
-
/** A kit set up in a project: its flow (by name) and its
|
|
107
|
+
/** A kit set up in a project: its flow (by name) and its subagent, when both are there. */
|
|
108
108
|
export function kitInstalled(store, kit, repo) {
|
|
109
|
-
const mate = store.
|
|
109
|
+
const mate = store.subagentByHandle(repo, kit.subagent.handle);
|
|
110
110
|
const flow = store.listFlows([repo]).find(one => one.name === kit.flowName && one.state === "active") ?? null;
|
|
111
111
|
return mate === null || flow === null ? null : { flow, mate };
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
* Set a kit up: its
|
|
115
|
-
* flow, and its button triggers; then its
|
|
114
|
+
* Set a kit up: its subagent (kept if one of that name is already there), its
|
|
115
|
+
* flow, and its button triggers; then its subagent may use whichever of the
|
|
116
116
|
* kit's tools the project already has (reading free, writing asking first).
|
|
117
117
|
*/
|
|
118
118
|
export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
|
|
119
119
|
const made = setUpKitNow(store, kit, repo, by, now, dir);
|
|
120
120
|
if (!made.ok || made.said.endsWith("already set up here."))
|
|
121
121
|
return made;
|
|
122
|
-
const mate = store.
|
|
122
|
+
const mate = store.getSubagent(made.subagent);
|
|
123
123
|
const present = new Set(projectToolsOf(store, repo).map(one => one.name));
|
|
124
124
|
for (const tool of kit.tools.filter(one => present.has(one.tool))) {
|
|
125
125
|
try {
|
|
@@ -129,20 +129,20 @@ export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
|
|
|
129
129
|
}
|
|
130
130
|
return made;
|
|
131
131
|
}
|
|
132
|
-
/** The same, all at once (a chat confirmation can't wait on a tool): its
|
|
132
|
+
/** The same, all at once (a chat confirmation can't wait on a tool): its subagent is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
|
|
133
133
|
export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false) {
|
|
134
134
|
const had = kitInstalled(store, kit, repo);
|
|
135
135
|
if (had !== null)
|
|
136
|
-
return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id,
|
|
137
|
-
let mate = store.
|
|
136
|
+
return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id, subagent: had.mate.id };
|
|
137
|
+
let mate = store.subagentByHandle(repo, kit.subagent.handle);
|
|
138
138
|
if (mate === null) {
|
|
139
|
-
const template =
|
|
139
|
+
const template = SUBAGENT_TEMPLATES.find(one => one.id === kit.subagent.template);
|
|
140
140
|
if (template === undefined)
|
|
141
|
-
return { ok: false, said: "That kit's
|
|
142
|
-
const made =
|
|
141
|
+
return { ok: false, said: "That kit's subagent template is missing." };
|
|
142
|
+
const made = createSubagentFrom(store, { repo, template: template.id, by }, now);
|
|
143
143
|
if (!made.ok)
|
|
144
144
|
return { ok: false, said: made.said };
|
|
145
|
-
mate = store.
|
|
145
|
+
mate = store.getSubagent(made.id);
|
|
146
146
|
}
|
|
147
147
|
let definition;
|
|
148
148
|
try {
|
|
@@ -158,7 +158,7 @@ export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false)
|
|
|
158
158
|
if (grantByName)
|
|
159
159
|
for (const tool of projectToolsOf(store, repo).filter(one => kit.tools.some(each => each.tool === one.name)))
|
|
160
160
|
grantListed(store, mate, tool, null, by, now);
|
|
161
|
-
return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId,
|
|
161
|
+
return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId, subagent: mate.id };
|
|
162
162
|
}
|
|
163
163
|
/** What's set up and what's left, read fresh each time. */
|
|
164
164
|
export function kitChecklist(store, kit, repo, dir) {
|
|
@@ -167,7 +167,7 @@ export function kitChecklist(store, kit, repo, dir) {
|
|
|
167
167
|
return [];
|
|
168
168
|
const name = nameOf(set.mate);
|
|
169
169
|
const steps = [
|
|
170
|
-
{ id: "
|
|
170
|
+
{ id: "subagent", done: true, said: `${name} joined the team`, href: `/settings/lead/subagents/${set.mate.id}` },
|
|
171
171
|
{ id: "flow", done: true, said: `The ${kit.flowName} flow is ready`, href: `/flows/${set.flow.id}` },
|
|
172
172
|
];
|
|
173
173
|
if (kit.email === true) {
|
|
@@ -184,18 +184,18 @@ export function kitChecklist(store, kit, repo, dir) {
|
|
|
184
184
|
const tools = projectToolsOf(store, repo);
|
|
185
185
|
for (const tool of kit.tools) {
|
|
186
186
|
const connected = tools.some(one => one.name === tool.tool);
|
|
187
|
-
const granted = store.
|
|
187
|
+
const granted = store.subagentGrant(set.mate.id, tool.tool) !== null;
|
|
188
188
|
steps.push(connected && granted
|
|
189
|
-
? { id: `tool-${tool.tool}`, done: true, said: `${name} can use ${tool.label}`, href: `/
|
|
189
|
+
? { id: `tool-${tool.tool}`, done: true, said: `${name} can use ${tool.label}`, href: `/settings/lead/subagents/${set.mate.id}#tools` }
|
|
190
190
|
: connected
|
|
191
|
-
? { id: `tool-${tool.tool}`, done: false, said: `Let ${name} use ${tool.label}, ${tool.why}`, href: `/
|
|
191
|
+
? { id: `tool-${tool.tool}`, done: false, said: `Let ${name} use ${tool.label}, ${tool.why}`, href: `/settings/lead/subagents/${set.mate.id}#tools` }
|
|
192
192
|
: { id: `tool-${tool.tool}`, done: false, said: `Connect ${tool.label}, ${tool.why}`, href: `/settings/tools?repo=${encodeURIComponent(repo)}&kit=${kit.id}&connect=${tool.tool}#connect`, action: "connect", tool: tool.tool });
|
|
193
193
|
}
|
|
194
194
|
const tried = store.flowCards(set.flow.id, true).length > 0;
|
|
195
195
|
steps.push({ id: "sample", done: tried, said: tried ? `${name} has worked a card` : `See ${name} work a sample card`, href: tried ? `/flows/${set.flow.id}` : null, ...(tried ? {} : { action: "sample" }) });
|
|
196
196
|
return steps;
|
|
197
197
|
}
|
|
198
|
-
/** A sample card in the kit's first zone, so its
|
|
198
|
+
/** A sample card in the kit's first zone, so its subagent starts on it at once. */
|
|
199
199
|
export function addKitSample(store, kit, repo, by, now) {
|
|
200
200
|
const set = kitInstalled(store, kit, repo);
|
|
201
201
|
if (set === null)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type LeadProposal, type Store } from "./store.js";
|
|
2
2
|
import { type ShapeOptions } from "./reply-shape.js";
|
|
3
|
-
import type {
|
|
4
|
-
export type
|
|
3
|
+
import type { SubscriptionLeadRunner } from "./subscription-chat.js";
|
|
4
|
+
export type LeadCliSeams = {
|
|
5
5
|
fetcher?: typeof fetch;
|
|
6
6
|
env?: Record<string, string | undefined>;
|
|
7
7
|
/** Lines the REPL reads instead of stdin (tests). */
|
|
8
8
|
lines?: AsyncIterable<string> | Iterable<string>;
|
|
9
9
|
clock?: () => Date;
|
|
10
10
|
/** Subscription harness seam; tests never consume a real membership turn. */
|
|
11
|
-
subscriptionRunner?:
|
|
11
|
+
subscriptionRunner?: SubscriptionLeadRunner;
|
|
12
12
|
};
|
|
13
|
-
export type
|
|
13
|
+
export type LeadCliInput = {
|
|
14
14
|
store: Store;
|
|
15
15
|
databaseFile: string;
|
|
16
16
|
write: (line: string) => void;
|
|
@@ -25,18 +25,18 @@ export type MateCliInput = {
|
|
|
25
25
|
end: boolean;
|
|
26
26
|
follow?: boolean;
|
|
27
27
|
ceilingUsd: number | undefined;
|
|
28
|
-
seams?:
|
|
28
|
+
seams?: LeadCliSeams;
|
|
29
29
|
/** Where evidence lives — a scout's report reads from here. */
|
|
30
30
|
evidenceRoot?: string;
|
|
31
31
|
/** The console's address(es): a reply's links there are named as its pages ("the task"), not by host. */
|
|
32
32
|
appOrigin?: ShapeOptions["appOrigin"];
|
|
33
33
|
};
|
|
34
|
-
export type
|
|
34
|
+
export type LeadCliResult = {
|
|
35
35
|
code: number;
|
|
36
36
|
reason?: string;
|
|
37
37
|
message?: string;
|
|
38
38
|
};
|
|
39
|
-
export declare const
|
|
39
|
+
export declare const LEAD_CLI_EXIT: {
|
|
40
40
|
readonly ok: 0;
|
|
41
41
|
readonly failed: 1;
|
|
42
42
|
readonly usage: 2;
|
|
@@ -49,7 +49,7 @@ export declare const MATE_CLI_EXIT: {
|
|
|
49
49
|
*/
|
|
50
50
|
export declare function answerContextLines(store: Store, payload: Record<string, unknown>): string[];
|
|
51
51
|
/** One line per proposal, numbered in thread order, for the operator to name. */
|
|
52
|
-
export declare function proposalLines(proposals: readonly
|
|
52
|
+
export declare function proposalLines(proposals: readonly LeadProposal[], repos: readonly string[], ordinal?: (proposal: LeadProposal) => number): string[];
|
|
53
53
|
/** The lines for one proposal, its answer context included when it is an answer. */
|
|
54
|
-
export declare function proposalBlock(store: Store, proposal:
|
|
55
|
-
export declare function
|
|
54
|
+
export declare function proposalBlock(store: Store, proposal: LeadProposal, repos: readonly string[], number: number): string[];
|
|
55
|
+
export declare function runLeadCli(input: LeadCliInput): Promise<LeadCliResult>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { configureLeadFollow, leadFollowStatus } from './lead-follow.js';
|
|
2
2
|
/**
|
|
3
3
|
* `toolroll chat` (mate arc §6): the same thread the console shows,
|
|
4
|
-
* driven from a terminal. The password is typed once — it mints the
|
|
4
|
+
* driven from a terminal. The password is typed once — it mints the lead
|
|
5
5
|
* session, the one ceremony a conversation gets — and every later turn
|
|
6
6
|
* debits that session without asking again. Confirming a card runs the
|
|
7
7
|
* same doors the console runs; password-class acts (approving a scope,
|
|
@@ -10,16 +10,16 @@ import { configureLeadFollow, leadFollowStatus } from './lead-follow.js';
|
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
11
|
import { readFileSync } from "node:fs";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
-
import {
|
|
13
|
+
import { LEAD_ASK_OTHER } from "./store.js";
|
|
14
14
|
import { CHAT_KEY_ENV, credentialKeyOf, isDirectChatProvider, priceForConfig, subscriptionCredentialKey } from "./converse.js";
|
|
15
15
|
import { verifyApproverByPassword } from "./principal.js";
|
|
16
|
-
import {
|
|
16
|
+
import { runLeadTurn } from "./lead.js";
|
|
17
17
|
import { voiceReply } from "./reply-shape.js";
|
|
18
|
-
import {
|
|
18
|
+
import { confirmLeadProposal, dismissLeadProposal } from "./lead-doors.js";
|
|
19
19
|
import { projectName } from "./project.js";
|
|
20
20
|
import { CHAT_CONTROLS, chatControlHref, isChatControl } from "./chat-controls.js";
|
|
21
21
|
import { CHAT_TASK_ACTIONS, isChatTaskAction } from "./chat-task-actions.js";
|
|
22
|
-
export const
|
|
22
|
+
export const LEAD_CLI_EXIT = { ok: 0, failed: 1, usage: 2, refused: 3 };
|
|
23
23
|
function money(microusd) {
|
|
24
24
|
return `$${(microusd / 1_000_000).toFixed(2)}`;
|
|
25
25
|
}
|
|
@@ -119,7 +119,7 @@ async function* linesOf(seams) {
|
|
|
119
119
|
reader.close();
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
export async function
|
|
122
|
+
export async function runLeadCli(input) {
|
|
123
123
|
const { store, write, json } = input;
|
|
124
124
|
const seams = input.seams ?? {};
|
|
125
125
|
const clock = seams.clock ?? (() => new Date());
|
|
@@ -132,7 +132,7 @@ export async function runMateCli(input) {
|
|
|
132
132
|
if (json)
|
|
133
133
|
write(JSON.stringify({ command: "chat", ...envelope }));
|
|
134
134
|
};
|
|
135
|
-
const refuse = (reason, message, code =
|
|
135
|
+
const refuse = (reason, message, code = LEAD_CLI_EXIT.refused) => {
|
|
136
136
|
if (json)
|
|
137
137
|
write(JSON.stringify({ ok: false, command: "chat", reason, message }));
|
|
138
138
|
else
|
|
@@ -154,22 +154,22 @@ export async function runMateCli(input) {
|
|
|
154
154
|
return refuse("no-key", `no ${directProvider} key — export ${CHAT_KEY_ENV[directProvider]}, or paste one on the console's chat page`);
|
|
155
155
|
const repos = [...input.repos];
|
|
156
156
|
if (repos.length === 0)
|
|
157
|
-
return refuse("empty-ceiling", "The lead has no projects to look at. Name some with --repo, or add projects in the console.",
|
|
157
|
+
return refuse("empty-ceiling", "The lead has no projects to look at. Name some with --repo, or add projects in the console.", LEAD_CLI_EXIT.usage);
|
|
158
158
|
const verified = verifyApproverByPassword(store, input.credentials.name, input.credentials.token, repos);
|
|
159
159
|
if (!verified.ok)
|
|
160
160
|
return refuse("unauthenticated", "that is not an approver, or the password does not match");
|
|
161
161
|
const who = verified.who;
|
|
162
162
|
let now = clock();
|
|
163
163
|
if (input.end) {
|
|
164
|
-
const turns = store.
|
|
165
|
-
const sessions = store.
|
|
166
|
-
const threads = store.
|
|
164
|
+
const turns = store.failLiveLeadTurnsFor(who.name, "ended", now);
|
|
165
|
+
const sessions = store.endLeadSessionsFor(who.name, who.name, now);
|
|
166
|
+
const threads = store.closeLeadThreadsFor(who.name, now);
|
|
167
167
|
emit({ ok: true, ended: { sessions, threads, turns } });
|
|
168
168
|
say(sessions === 0 ? "No conversation was open, so there was nothing to end." : "The conversation has ended and its history is forgotten.");
|
|
169
|
-
return { code:
|
|
169
|
+
return { code: LEAD_CLI_EXIT.ok };
|
|
170
170
|
}
|
|
171
|
-
store.
|
|
172
|
-
let session = store.
|
|
171
|
+
store.sweepStaleLeadTurns(now);
|
|
172
|
+
let session = store.activeLeadSession(who.name);
|
|
173
173
|
const credentialKey = direct ? credentialKeyOf(directProvider, key) : subscriptionCredentialKey(subscriptionProvider);
|
|
174
174
|
if (session !== null && session.credentialKey !== credentialKey) {
|
|
175
175
|
// A session minted under another provider key cannot be spent by this
|
|
@@ -177,38 +177,38 @@ export async function runMateCli(input) {
|
|
|
177
177
|
return refuse("key-mismatch", "The open conversation was started with a different provider key. Use that key, or end it with --end and start a new one.");
|
|
178
178
|
}
|
|
179
179
|
if (session !== null && session.ceilingDigest !== who.ceilingDigest) {
|
|
180
|
-
store.
|
|
181
|
-
store.
|
|
180
|
+
store.endLeadSession(session.id, who.name, now);
|
|
181
|
+
store.closeLeadThreadsFor(who.name, now);
|
|
182
182
|
say("The projects you named differ from the open conversation's, so it ended. Starting a new one.");
|
|
183
183
|
session = null;
|
|
184
184
|
}
|
|
185
185
|
if (session === null) {
|
|
186
186
|
const ceilingUsd = direct ? (input.ceilingUsd ?? 5) : 0;
|
|
187
187
|
if (direct && (!Number.isFinite(ceilingUsd) || ceilingUsd <= 0 || ceilingUsd > 1_000))
|
|
188
|
-
return refuse("usage", "--ceiling-usd is a dollar amount between 0 and 1000",
|
|
188
|
+
return refuse("usage", "--ceiling-usd is a dollar amount between 0 and 1000", LEAD_CLI_EXIT.usage);
|
|
189
189
|
const ceilingMicrousd = Math.round(ceilingUsd * 1_000_000);
|
|
190
190
|
const termsDigest = createHash("sha256").update(`${ceilingMicrousd}\n${who.ceilingDigest}`).digest("hex");
|
|
191
|
-
const id = store.
|
|
192
|
-
session = store.
|
|
191
|
+
const id = store.mintLeadSession({ approver: who.name, approverGeneration: who.generation, credentialKey, ceilingMicrousd, ceilingDigest: who.ceilingDigest, termsDigest }, now);
|
|
192
|
+
session = store.getLeadSession(id);
|
|
193
193
|
if (session === null)
|
|
194
|
-
return refuse("failed", "A new conversation couldn't be started. Try again.",
|
|
194
|
+
return refuse("failed", "A new conversation couldn't be started. Try again.", LEAD_CLI_EXIT.failed);
|
|
195
195
|
if (!direct && input.ceilingUsd !== undefined)
|
|
196
196
|
say("the supplied --ceiling-usd value is ignored for membership usage");
|
|
197
|
-
say(`
|
|
197
|
+
say(`lead conversation started: ${direct ? `up to ${money(ceilingMicrousd)}` : "subscription usage (no dollar limit)"} over ${repos.map(one => projectName(one)).join(", ")} — live until you end it`);
|
|
198
198
|
if (direct)
|
|
199
199
|
say(`(the weekly chat limit, ${money(config.weeklyCeilingMicrousd)}, still applies)`);
|
|
200
200
|
}
|
|
201
201
|
else {
|
|
202
|
-
say(`
|
|
202
|
+
say(`lead conversation live: ${direct ? `${money(session.spentMicrousd)} of ${money(session.ceilingMicrousd)} spent` : "subscription usage (no dollar limit)"} — live until you end it`);
|
|
203
203
|
}
|
|
204
|
-
const thread = store.
|
|
204
|
+
const thread = store.openLeadThread(who.name, who.ceilingDigest, now).thread;
|
|
205
205
|
if (input.follow !== undefined) {
|
|
206
206
|
if (!configureLeadFollow(store, who, session, thread, input.follow, now))
|
|
207
207
|
return refuse("standing", "Conversation access changed.");
|
|
208
208
|
say(leadFollowStatus(store, who.name).detail);
|
|
209
209
|
emit({ ok: true, follow: leadFollowStatus(store, who.name) });
|
|
210
210
|
}
|
|
211
|
-
const pendingProposals = () => store.
|
|
211
|
+
const pendingProposals = () => store.listLeadProposals(thread.id, ["pending"]);
|
|
212
212
|
// Ordinals are assigned once per proposal and never reused within this
|
|
213
213
|
// run (slice-2 review, finding 4): `confirm 2` means the card printed as
|
|
214
214
|
// 2, whatever the console did to its neighbours meanwhile.
|
|
@@ -224,7 +224,7 @@ export async function runMateCli(input) {
|
|
|
224
224
|
const byOrdinal = (ordinal) => {
|
|
225
225
|
for (const [id, n] of ordinals)
|
|
226
226
|
if (n === ordinal)
|
|
227
|
-
return store.
|
|
227
|
+
return store.getLeadProposal(id);
|
|
228
228
|
return null;
|
|
229
229
|
};
|
|
230
230
|
const printProposals = () => {
|
|
@@ -239,12 +239,12 @@ export async function runMateCli(input) {
|
|
|
239
239
|
say(line);
|
|
240
240
|
};
|
|
241
241
|
const turn = async (message) => {
|
|
242
|
-
const live = store.
|
|
242
|
+
const live = store.activeLeadSession(who.name);
|
|
243
243
|
if (live === null) {
|
|
244
244
|
const outcome = { ok: false, refused: "session-ended", message: "This conversation has ended. Run chat again to start a new one." };
|
|
245
245
|
return outcome;
|
|
246
246
|
}
|
|
247
|
-
return
|
|
247
|
+
return runLeadTurn({ store, who, session: live, thread, config, key, message, channel: "terminal", ...(seams.fetcher === undefined ? {} : { fetcher: seams.fetcher }), ...(seams.subscriptionRunner === undefined ? {} : { subscriptionRunner: seams.subscriptionRunner }), ...(input.evidenceRoot === undefined ? {} : { evidenceRoot: input.evidenceRoot }), clock });
|
|
248
248
|
};
|
|
249
249
|
const report = (outcome, message) => {
|
|
250
250
|
if (outcome.ok && outcome.replayed) {
|
|
@@ -253,14 +253,14 @@ export async function runMateCli(input) {
|
|
|
253
253
|
return;
|
|
254
254
|
}
|
|
255
255
|
if (outcome.ok) {
|
|
256
|
-
const ask = store.
|
|
256
|
+
const ask = store.leadAsk(outcome.turn);
|
|
257
257
|
emit({ ok: true, turn: outcome.turn, reply: outcome.reply, activity: outcome.activity, steps: outcome.steps, settledMicrousd: outcome.settledMicrousd, proposals: pendingProposals().map(one => ({ id: one.id, ordinal: ordinalOf(one), kind: one.kind, payload: one.payload })),
|
|
258
|
-
...(ask === null ? {} : { ask: { question: ask.question, options: [...ask.options,
|
|
258
|
+
...(ask === null ? {} : { ask: { question: ask.question, options: [...ask.options, LEAD_ASK_OTHER] } }) });
|
|
259
259
|
say(` ${outcome.activity}`);
|
|
260
260
|
say(voiceReply(outcome.reply, "terminal", { appOrigin: input.appOrigin ?? null, ...(message === undefined ? {} : { asked: message }) }));
|
|
261
261
|
// The lead's question: the terminal has no buttons, so the options are listed to type back.
|
|
262
262
|
if (ask !== null)
|
|
263
|
-
say(`${ask.question}\n${ask.options.map(one => ` · ${one}`).join("\n")}\n · ${
|
|
263
|
+
say(`${ask.question}\n${ask.options.map(one => ` · ${one}`).join("\n")}\n · ${LEAD_ASK_OTHER} (type your answer)`);
|
|
264
264
|
printProposals();
|
|
265
265
|
return;
|
|
266
266
|
}
|
|
@@ -270,7 +270,7 @@ export async function runMateCli(input) {
|
|
|
270
270
|
if (input.say !== undefined) {
|
|
271
271
|
const outcome = await turn(input.say);
|
|
272
272
|
report(outcome, input.say);
|
|
273
|
-
return { code: outcome.ok ?
|
|
273
|
+
return { code: outcome.ok ? LEAD_CLI_EXIT.ok : LEAD_CLI_EXIT.refused };
|
|
274
274
|
}
|
|
275
275
|
// The REPL. Text is a turn; a few words are acts on the cards.
|
|
276
276
|
say("say something, or: proposals · confirm N · dismiss N · open N · end · quit");
|
|
@@ -311,12 +311,12 @@ export async function runMateCli(input) {
|
|
|
311
311
|
openTask(proposal);
|
|
312
312
|
}
|
|
313
313
|
else if (verb === "dismiss") {
|
|
314
|
-
const done =
|
|
314
|
+
const done = dismissLeadProposal(store, who, proposal.id, now);
|
|
315
315
|
emit({ ok: done, act: "dismiss", proposal: proposal.id });
|
|
316
316
|
say(done ? `dismissed ${act[2]}` : "that proposal was already acted on");
|
|
317
317
|
}
|
|
318
318
|
else {
|
|
319
|
-
const outcome =
|
|
319
|
+
const outcome = confirmLeadProposal(store, who, proposal.id, now, { confirm: act[3] !== undefined, via: "cli", chatProvider: () => {
|
|
320
320
|
const current = store.getChatConfig();
|
|
321
321
|
return current === null ? null : { config: current, key: isDirectChatProvider(current.provider) ? keyFor(current, input.databaseFile, env) : null };
|
|
322
322
|
}, ...(input.evidenceRoot === undefined ? {} : { evidenceRoot: input.evidenceRoot }) });
|
|
@@ -338,12 +338,12 @@ export async function runMateCli(input) {
|
|
|
338
338
|
if (word === "quit" || word === "exit")
|
|
339
339
|
break;
|
|
340
340
|
if (word === "end") {
|
|
341
|
-
store.
|
|
342
|
-
store.
|
|
343
|
-
store.
|
|
341
|
+
store.failLiveLeadTurnsFor(who.name, "ended", now);
|
|
342
|
+
store.endLeadSessionsFor(who.name, who.name, now);
|
|
343
|
+
store.closeLeadThreadsFor(who.name, now);
|
|
344
344
|
say("The conversation has ended and its history is forgotten.");
|
|
345
345
|
emit({ ok: true, ended: true });
|
|
346
|
-
return { code:
|
|
346
|
+
return { code: LEAD_CLI_EXIT.ok };
|
|
347
347
|
}
|
|
348
348
|
if (word === "help" || word === "?") {
|
|
349
349
|
say("say something, or: proposals · confirm N · dismiss N · open N · end · quit");
|
|
@@ -351,5 +351,5 @@ export async function runMateCli(input) {
|
|
|
351
351
|
}
|
|
352
352
|
report(await turn(line), line);
|
|
353
353
|
}
|
|
354
|
-
return { code:
|
|
354
|
+
return { code: LEAD_CLI_EXIT.ok };
|
|
355
355
|
}
|
package/dist/lead-commitments.js
CHANGED
|
@@ -90,7 +90,7 @@ export function getCommitment(store, id) {
|
|
|
90
90
|
/** The reply that made a promise was shown: its turn is over, and it was answered or its text reached the conversation
|
|
91
91
|
* before it failed. A turn still running has shown nothing yet, whatever it has written so far. */
|
|
92
92
|
const SHOWN = (turn) => `EXISTS (SELECT 1 FROM mate_turn t WHERE t.id = ${turn} AND (t.state = 'answered'
|
|
93
|
-
OR t.state = 'failed' AND EXISTS (SELECT 1 FROM
|
|
93
|
+
OR t.state = 'failed' AND EXISTS (SELECT 1 FROM lead_message m WHERE m.turn = t.id AND m.role = 'assistant')))`;
|
|
94
94
|
/** A promise only counts once the reply that made it was shown: one from a reply still being written, or one that
|
|
95
95
|
* failed without reaching the owner, was never heard. */
|
|
96
96
|
const HEARD = `(c.turn IS NULL OR ${SHOWN('c.turn')})`;
|
|
@@ -173,7 +173,7 @@ export function checkLeadCommitments(store, now, root) {
|
|
|
173
173
|
continue;
|
|
174
174
|
}
|
|
175
175
|
// A reply still being written may yet be delivered; one that was not is dropped above.
|
|
176
|
-
const turn = one.turn === null ? null : store.
|
|
176
|
+
const turn = one.turn === null ? null : store.getLeadTurn(one.turn);
|
|
177
177
|
if (turn !== null && (turn.state === 'queued' || turn.state === 'running'))
|
|
178
178
|
continue;
|
|
179
179
|
const account = store.accountOf(one.owner);
|
|
@@ -181,7 +181,7 @@ export function checkLeadCommitments(store, now, root) {
|
|
|
181
181
|
close('cancelled', 'Access to this project ended.');
|
|
182
182
|
continue;
|
|
183
183
|
}
|
|
184
|
-
const thread = store.
|
|
184
|
+
const thread = store.getLeadThread(one.thread);
|
|
185
185
|
if (thread === null || thread.approver !== one.owner || thread.closedAt !== null) {
|
|
186
186
|
close('cancelled', 'The conversation it was promised in was closed.');
|
|
187
187
|
continue;
|
|
@@ -203,7 +203,7 @@ export function checkLeadCommitments(store, now, root) {
|
|
|
203
203
|
return;
|
|
204
204
|
// The shared conversation always keeps the line; a promise made on a phone or team chat is also said there, as
|
|
205
205
|
// the lead's own message to its owner, on that chat alone (the usual delivery receipts apply).
|
|
206
|
-
store.
|
|
206
|
+
store.appendLeadMessage({ thread: one.thread, turn: null, role: 'assistant', text: line }, now);
|
|
207
207
|
if (one.channel !== 'chat')
|
|
208
208
|
store.enqueueNotification({ dedupeKey: `${PROMISE_KEY}${one.channel}:${one.id}`, kind: LEAD_SAY_KIND, subject: leadNameOf(store, one.owner),
|
|
209
209
|
body: line.slice(0, 300), recipient: one.owner, source: { installation: true } }, now);
|