toolroll 0.9.55 → 0.9.56
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 +10 -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/browser/workspace.js +5 -5
- package/dist/browser-workspace.d.ts +12 -12
- 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 +27 -34
- 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-warmth.d.ts +2 -2
- 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/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/{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 +35 -34
- 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/recipe-ui.js +1 -1
- 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 +97 -97
- 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-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 +6 -5
- 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 +197 -249
- package/dist/store.js +747 -3084
- 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/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 +8 -8
- 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.js +1 -1
- 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 +9 -30
- 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/package.json +1 -2
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -21
package/dist/limits-ui.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare function limitsView(windows: readonly {
|
|
|
20
20
|
observedAt: string;
|
|
21
21
|
}[], budgets: readonly BudgetState[], names: {
|
|
22
22
|
project: (repo: string) => string;
|
|
23
|
-
|
|
23
|
+
subagent: (id: number) => string;
|
|
24
24
|
}, now: Date): BrowserLimits | null;
|
|
25
25
|
/** The same tiles for the page without scripts. */
|
|
26
26
|
export declare function limitsHtml(limits: BrowserLimits | null): Html;
|
package/dist/limits-ui.js
CHANGED
|
@@ -38,7 +38,7 @@ export function limitsView(windows, budgets, names, now) {
|
|
|
38
38
|
for (const budget of budgets) {
|
|
39
39
|
const over = budget.spentMicrousd >= budget.limitMicrousd;
|
|
40
40
|
const name = budget.scope === "installation" ? "Everything" : budget.scope === "project" ? names.project(budget.key)
|
|
41
|
-
: budget.scope === "
|
|
41
|
+
: budget.scope === "subagent" ? names.subagent(Number(budget.key)) : budget.key;
|
|
42
42
|
tiles.push({
|
|
43
43
|
key: `budget:${budget.id}`, name, window: "Budget", value: usd(budget.spentMicrousd), unit: `of ${usd(budget.limitMicrousd)}`,
|
|
44
44
|
percent: budget.percent, tone: over ? (budget.hardStop ? "danger" : "warning") : budget.percent >= 80 ? "warning" : "neutral", marks: [50, 80], href: "/spend",
|
package/dist/mcp-connect.d.ts
CHANGED
|
@@ -159,7 +159,7 @@ export declare function finishConnect(store: Store, visit: ConnectVisit, code: s
|
|
|
159
159
|
ok: false;
|
|
160
160
|
said: string;
|
|
161
161
|
}>;
|
|
162
|
-
/** Keep connected services signed in: any token expiring within ten minutes is refreshed (the worker's pass, and before a
|
|
162
|
+
/** Keep connected services signed in: any token expiring within ten minutes is refreshed (the worker's pass, and before a subagent's call). */
|
|
163
163
|
export declare function refreshConnections(store: Store, repos: readonly string[], now: Date, options?: {
|
|
164
164
|
fetcher?: Fetch;
|
|
165
165
|
home?: string;
|
package/dist/mcp-connect.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* with this console's callback, and sends the person to the service to sign
|
|
9
9
|
* in. The callback trades the code for tokens, which live in the tool's own
|
|
10
10
|
* secrets file (never the database, never a log), and the tool joins the
|
|
11
|
-
* project like any other: tested, listed, and given to
|
|
11
|
+
* project like any other: tested, listed, and given to subagents under rules.
|
|
12
12
|
* The worker refreshes tokens before they expire.
|
|
13
13
|
*/
|
|
14
14
|
import { createHash, randomBytes } from "node:crypto";
|
|
@@ -382,10 +382,10 @@ export async function finishConnect(store, visit, code, now, options = {}) {
|
|
|
382
382
|
if (options.test === false)
|
|
383
383
|
return { ok: true, said: `${service.label} is connected to ${to}.` };
|
|
384
384
|
const tested = await testToolOf(store, visit.repo, spec.name, now, { home, ...(options.omitEnv === undefined ? {} : { omitEnv: options.omitEnv }) });
|
|
385
|
-
return tested?.ok ? { ok: true, said: `${service.label} is connected to ${to}: ${tested.tools.length} action${tested.tools.length === 1 ? "" : "s"}. Let a
|
|
385
|
+
return tested?.ok ? { ok: true, said: `${service.label} is connected to ${to}: ${tested.tools.length} action${tested.tools.length === 1 ? "" : "s"}. Let a subagent use it from its page.` }
|
|
386
386
|
: { ok: true, said: `${service.label} is signed in for ${to}, but its test didn't pass yet: ${tested?.problem ?? "no answer"}.` };
|
|
387
387
|
}
|
|
388
|
-
/** Keep connected services signed in: any token expiring within ten minutes is refreshed (the worker's pass, and before a
|
|
388
|
+
/** Keep connected services signed in: any token expiring within ten minutes is refreshed (the worker's pass, and before a subagent's call). */
|
|
389
389
|
export async function refreshConnections(store, repos, now, options = {}) {
|
|
390
390
|
const fetcher = options.fetcher ?? fetch, home = options.home ?? homedir();
|
|
391
391
|
const report = { refreshed: 0, problems: [] };
|
package/dist/mcp-core.js
CHANGED
|
@@ -17,7 +17,7 @@ import { GATEWAY_TOOL_INPUTS, GATEWAY_TOOL_OUTPUTS } from "./contracts/gateway-t
|
|
|
17
17
|
import { reportToolOutput } from "./contracts/lead-tools.js";
|
|
18
18
|
import { PACKAGE_VERSION } from "./version.js";
|
|
19
19
|
import { authenticateCoordinator, fileCoordinatorProposal, statusFor, listTasksFor, taskDetailFor, } from "./coordinator.js";
|
|
20
|
-
import { ISO_STAMP_RULE, decisionOver, decisionsOver, labelRepos, queueOver, recapOver } from "./
|
|
20
|
+
import { ISO_STAMP_RULE, decisionOver, decisionsOver, labelRepos, queueOver, recapOver } from "./lead-tools.js";
|
|
21
21
|
import { proposeAsCoordinator } from "./coordinator-proposals.js";
|
|
22
22
|
export const MODERN = "2026-07-28";
|
|
23
23
|
export const LEGACY = "2025-11-25";
|
package/dist/memory-pass.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Store } from './store.js';
|
|
2
|
-
import {
|
|
2
|
+
import { performSubscriptionLeadRequest } from './subscription-chat.js';
|
|
3
3
|
import { type MemoryProposal, type Verdict } from './contracts/memory-pass.js';
|
|
4
4
|
export type { MemoryProposal, Verdict };
|
|
5
5
|
/** One session's trace, in characters (TEXT_LIMITS.memoryTrace). */
|
|
@@ -66,7 +66,7 @@ export declare function defaultAnalyzer(store: Store, options: {
|
|
|
66
66
|
env?: NodeJS.ProcessEnv;
|
|
67
67
|
fetcher?: typeof fetch;
|
|
68
68
|
timeoutMs?: number;
|
|
69
|
-
runner?: Parameters<typeof
|
|
69
|
+
runner?: Parameters<typeof performSubscriptionLeadRequest>[1];
|
|
70
70
|
}): MemoryAnalyzer;
|
|
71
71
|
/**
|
|
72
72
|
* Read the analyser's answer with the tolerant parse-side schema, then keep only claims whose quote
|
package/dist/memory-pass.js
CHANGED
|
@@ -17,8 +17,8 @@ import { basename, join } from 'node:path';
|
|
|
17
17
|
import { scanForSecrets, readVerifiedArtifact } from './evidence.js';
|
|
18
18
|
import { changeKnowledge, knowledgeView } from './project-knowledge.js';
|
|
19
19
|
import { listDecisions, recordDecision } from './project-memory.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
20
|
+
import { composeLeadRequest, performLeadRequest, isDirectChatProvider, CHAT_KEY_ENV } from './converse.js';
|
|
21
|
+
import { performSubscriptionLeadRequest } from './subscription-chat.js';
|
|
22
22
|
import { TEXT_LIMITS } from './text-limits.js';
|
|
23
23
|
import { contractError, parseContract } from './contracts/contract.js';
|
|
24
24
|
import { MEMORY_VERDICT_MODEL_SCHEMA, MEMORY_VERDICT_VERSION, memoryEvidenceSchema, memoryProposalSchema, memoryVerdictReadSchema, readSavedVerdict, savedVerdictSchema } from './contracts/memory-pass.js';
|
|
@@ -138,11 +138,11 @@ function fileSources(repo, kind, home) {
|
|
|
138
138
|
/** The plane's own sessions: lead threads that touched the project, and finished crew runs with their handoff and check output. */
|
|
139
139
|
function planeSources(store, repo, evidenceRoot) {
|
|
140
140
|
const out = [];
|
|
141
|
-
const threads = store.handle.prepare(`SELECT c.thread AS thread, c.id AS conversation, MAX(m.id) AS last, MAX(m.created_at) AS at FROM team_conversation c JOIN
|
|
141
|
+
const threads = store.handle.prepare(`SELECT c.thread AS thread, c.id AS conversation, MAX(m.id) AS last, MAX(m.created_at) AS at FROM team_conversation c JOIN lead_message m ON m.thread=c.thread
|
|
142
142
|
WHERE EXISTS (SELECT 1 FROM json_each(c.projects_json) WHERE value=?) GROUP BY c.thread`).all(repo);
|
|
143
143
|
for (const row of threads) {
|
|
144
144
|
const thread = Number(row['thread']);
|
|
145
|
-
out.push({ id: `lead:${thread}:${Number(row['last'])}`, repo, kind: 'lead', source: `conversation:${String(row['conversation'])}`, at: String(row['at']), trace: () => redact(store.handle.prepare('SELECT role,text FROM
|
|
145
|
+
out.push({ id: `lead:${thread}:${Number(row['last'])}`, repo, kind: 'lead', source: `conversation:${String(row['conversation'])}`, at: String(row['at']), trace: () => redact(store.handle.prepare('SELECT role,text FROM lead_message WHERE thread=? ORDER BY id LIMIT 400').all(thread).map(m => `${m['role']}: ${clip(String(m['text']), 4000)}`).join('\n')) });
|
|
146
146
|
}
|
|
147
147
|
const runs = store.handle.prepare(`SELECT r.id, r.role, r.finished_at, r.handoff, t.external_id FROM run r JOIN task_ref t ON t.id=r.task_ref WHERE t.repo=? AND r.role IN ('builder','repair') AND r.outcome IS NOT NULL ORDER BY r.id DESC LIMIT 100`).all(repo);
|
|
148
148
|
for (const row of runs) {
|
|
@@ -207,12 +207,12 @@ export function defaultAnalyzer(store, options) {
|
|
|
207
207
|
}
|
|
208
208
|
if (!key)
|
|
209
209
|
return { ok: false, problem: `No ${config.provider} key is available for the backward pass.` };
|
|
210
|
-
const request =
|
|
211
|
-
const answer = await
|
|
210
|
+
const request = composeLeadRequest({ provider: config.provider, model: config.model, key, system, dataDocument, history, tools: [] });
|
|
211
|
+
const answer = await performLeadRequest(request, config.provider, AbortSignal.timeout(options.timeoutMs ?? 120_000), options.fetcher);
|
|
212
212
|
return answer.ok ? { ok: true, text: answer.answer.text } : { ok: false, problem: answer.problem };
|
|
213
213
|
}
|
|
214
214
|
// The verdict's own schema is the harness's structured output, so the answer is the verdict itself.
|
|
215
|
-
const answer = await
|
|
215
|
+
const answer = await performSubscriptionLeadRequest({ provider: config.provider, model: config.model, system, dataDocument, history, tools: [], outputSchema: MEMORY_VERDICT_MODEL_SCHEMA, timeoutMs: options.timeoutMs ?? 180_000 }, options.runner);
|
|
216
216
|
return answer.ok ? { ok: true, text: answer.answer.text } : { ok: false, problem: answer.problem };
|
|
217
217
|
};
|
|
218
218
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Saved notifications, read and told apart without a store: the row readers, the lifecycle key and the predicates
|
|
2
2
|
* the store and the chat core (chat-messages.ts) share. A leaf: it imports types only. */
|
|
3
|
-
import type {
|
|
3
|
+
import type { LeadProposal, Notification, NotificationReceipt } from "./store.js";
|
|
4
4
|
/** The one task a card is about: a task its confirmation created (a new
|
|
5
5
|
* task, a revision), else the task it names (and, for feedback on a result,
|
|
6
6
|
* that result). Null for a card about no single task. A pointer for
|
|
7
7
|
* replies, never an authority. */
|
|
8
|
-
export declare function proposalTaskOf(proposal:
|
|
8
|
+
export declare function proposalTaskOf(proposal: LeadProposal | null): {
|
|
9
9
|
task: string;
|
|
10
10
|
run: number | null;
|
|
11
11
|
} | null;
|
package/dist/operate.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { FetchLike } from "./flow-share.js";
|
|
|
7
7
|
import { type Store } from "./store.js";
|
|
8
8
|
import { type DispatchAdapter } from "./sync.js";
|
|
9
9
|
import { type TelegramTransport } from "./telegram.js";
|
|
10
|
-
import { type
|
|
10
|
+
import { type LeadCliSeams } from "./lead-cli.js";
|
|
11
11
|
import { type Principal, type RemoteSource } from "./operate-remote.js";
|
|
12
12
|
import { type PublishExec } from "./publish.js";
|
|
13
13
|
import { type Runner as CommandRunner } from "./builder.js";
|
|
@@ -71,8 +71,8 @@ export type OperateOptions = {
|
|
|
71
71
|
flowFetch?: FetchLike;
|
|
72
72
|
/** Injected by tests: how `integrations` checks reach services (fetch, gh, sign-in checks, mail servers). */
|
|
73
73
|
integrationIo?: Partial<IntegrationIo>;
|
|
74
|
-
/** Injected by tests: the
|
|
75
|
-
|
|
74
|
+
/** Injected by tests: the lead's provider fetch, key environment, and stdin lines. */
|
|
75
|
+
leadSeams?: LeadCliSeams;
|
|
76
76
|
/** Test seam for the short indexed wait loop. */
|
|
77
77
|
waitSleep?: (milliseconds: number) => Promise<void>;
|
|
78
78
|
/** Injected by tests: `onboard`'s home folder, terminal and probes. */
|
package/dist/operate.js
CHANGED
|
@@ -22,7 +22,7 @@ import { checkoutPlan, cleanCheckouts, discardCheckout, finishedCheckouts, slimK
|
|
|
22
22
|
import { closeOutCheckouts, closeOutRuns, stoppedWords } from "./run-closeout.js";
|
|
23
23
|
import { MIN_DAYS, RETENTION_KINDS, countWords, dailyRetention, isRetentionKind, lastSweepAt, parsePeriod, periodLabel, periodWords, retentionPlan, sweepWords } from "./retention.js";
|
|
24
24
|
import { checkPolicy, parseList, policyParts } from "./policy.js";
|
|
25
|
-
import { billingOf, budgetHoldWords, budgetLabel, budgetStates, monthNamed, monthOf, spendItems,
|
|
25
|
+
import { billingOf, budgetHoldWords, budgetLabel, budgetStates, monthNamed, monthOf, spendItems, subagentNames, usd as spendUsd } from "./spend.js";
|
|
26
26
|
import { spendCsv } from "./spend-ui.js";
|
|
27
27
|
import { buildExport, exportSummary, exportZip, writeExportFolder } from "./export.js";
|
|
28
28
|
import { startBudgetAlerts } from "./budget-alerts.js";
|
|
@@ -43,8 +43,8 @@ import { origin, readMonitoring } from "./monitoring-settings.js";
|
|
|
43
43
|
import { logEvent } from "./log.js";
|
|
44
44
|
import { FLOW_EVERY_MS, flowHousekeeping, moveCards, moveCardsAfter } from "./flow-cadence.js";
|
|
45
45
|
import { readHooksBase, scheduleFromWords } from "./flow-triggers.js";
|
|
46
|
-
import {
|
|
47
|
-
import { runRequestedUndos,
|
|
46
|
+
import { sendSubagentSummaries } from "./subagent-admin.js";
|
|
47
|
+
import { runRequestedUndos, sendSubagentWeeklies } from "./subagent-week.js";
|
|
48
48
|
import { refreshConnections } from "./mcp-connect.js";
|
|
49
49
|
import { followDiscord } from "./discord.js";
|
|
50
50
|
import { followTeams } from "./teams.js";
|
|
@@ -156,8 +156,8 @@ import { bridgePass, clearBotToken, createTransport, followBridge, hashPairingCo
|
|
|
156
156
|
import { scanRepo } from "./capscan.js";
|
|
157
157
|
import { computeGaps, describeCapability } from "./gaps.js";
|
|
158
158
|
import { ask as promptAsk, askHidden as promptHidden, confirm as promptConfirm, interactive as promptInteractive, underAgent } from "./prompt.js";
|
|
159
|
-
import {
|
|
160
|
-
import { confirmCoordinatorProposal, dismissCoordinatorProposal } from "./
|
|
159
|
+
import { runLeadCli, answerContextLines } from "./lead-cli.js";
|
|
160
|
+
import { confirmCoordinatorProposal, dismissCoordinatorProposal } from "./lead-doors.js";
|
|
161
161
|
import { verifyApproverByPassword as approverByPassword } from "./principal.js";
|
|
162
162
|
import { activeRemote, REMOTE_MESSAGES, REMOTE_REFUSED_FLAGS, REMOTE_SCOPES, RemoteRefusal, remoteAllows, remoteLensOf, remoteProjectOf, remoteRowOf, remoteSecret, reproveRemote, withRemote } from "./operate-remote.js";
|
|
163
163
|
import { projectAuthority } from "./project-access.js";
|
|
@@ -256,9 +256,9 @@ export const OPERATE_HELP = `toolroll — operating the queue
|
|
|
256
256
|
toolroll integrations [--json] which integrations work: Connected, Not set up or Broken, and what to do (--saved: no new checks)
|
|
257
257
|
toolroll monitoring where the audit stream and traces go, and how each destination is doing
|
|
258
258
|
toolroll check-progress <run> current or final approved check progress
|
|
259
|
-
toolroll spend [--month YYYY-MM] [--csv] what agent work cost, by project, person,
|
|
259
|
+
toolroll spend [--month YYYY-MM] [--csv] what agent work cost, by project, person, subagent and model
|
|
260
260
|
toolroll export --out <path> [--zip] everything Toolroll knows, in a folder or .zip (no secrets)
|
|
261
|
-
toolroll budget list|set|remove monthly budgets (--all | --project <p> | --person <name> | --
|
|
261
|
+
toolroll budget list|set|remove monthly budgets (--all | --project <p> | --person <name> | --subagent <id>) --usd <n> [--alerts-only]
|
|
262
262
|
toolroll notifications [quiet|all] how chats reach you: only when you're needed (the default), or every step
|
|
263
263
|
toolroll notifications digest <HH:MM>|off one evening message: what finished, what waits, what failed
|
|
264
264
|
toolroll notifications mute|unmute --repo <p> no pings for a project; the console and digest keep it
|
|
@@ -477,7 +477,7 @@ Agents — which provider and model each phase runs on
|
|
|
477
477
|
toolroll chat --as <you> [--repo <path>…] [--say "…"] [--end]
|
|
478
478
|
[--ceiling-usd <n>] [--json] (password at the prompt;
|
|
479
479
|
--token <t> only for scripts — it lands in shell history)
|
|
480
|
-
|
|
480
|
+
your lead: one conversation across your projects, the same thread the
|
|
481
481
|
console shows; the password mints a spending session once; it reads
|
|
482
482
|
and proposes, you confirm cards (confirm N / dismiss N / open N;
|
|
483
483
|
confirm N yes for an irreversible answer)
|
|
@@ -625,7 +625,7 @@ export const OPERATE_VALUE_FLAGS = new Set([
|
|
|
625
625
|
// v103: the ledger chain and evidence packs.
|
|
626
626
|
"checkpoint", "out", "from", "to",
|
|
627
627
|
// v105: spend and budgets.
|
|
628
|
-
"month", "usd", "person", "teammate",
|
|
628
|
+
"month", "usd", "person", "subagent", "teammate",
|
|
629
629
|
// Sprint 8: the organisation policy.
|
|
630
630
|
"providers", "models", "tools",
|
|
631
631
|
"project-root", "schedule", "ceiling", "require",
|
|
@@ -869,7 +869,7 @@ async function dispatchOn(command, positional, flags, repoList, base, options, e
|
|
|
869
869
|
...(options.flowFetch === undefined ? {} : { flowFetch: options.flowFetch }),
|
|
870
870
|
...(options.integrationIo === undefined ? {} : { integrationIo: options.integrationIo }),
|
|
871
871
|
...(options.shouldStop === undefined ? {} : { shouldStop: options.shouldStop }),
|
|
872
|
-
...(options.
|
|
872
|
+
...(options.leadSeams === undefined ? {} : { leadSeams: options.leadSeams }),
|
|
873
873
|
...(options.waitSleep === undefined ? {} : { waitSleep: options.waitSleep }),
|
|
874
874
|
...(options.releaseIo === undefined ? {} : { releaseIo: options.releaseIo }),
|
|
875
875
|
...(options.installBin === undefined ? {} : { installBin: options.installBin }),
|
|
@@ -1023,7 +1023,7 @@ function telegramCanDeliver(context, token) {
|
|
|
1023
1023
|
function telegramConversation(context, options = {}) {
|
|
1024
1024
|
return {
|
|
1025
1025
|
evidenceRoot: context.evidenceRoot,
|
|
1026
|
-
...(context.
|
|
1026
|
+
...(context.leadSeams?.subscriptionRunner === undefined ? {} : { subscriptionRunner: context.leadSeams.subscriptionRunner }),
|
|
1027
1027
|
// Re-read on every card and every `/task`: the same console-url chat
|
|
1028
1028
|
// links use, held to an https origin, and — inside `up`, where this
|
|
1029
1029
|
// process also serves the console — equal to that console's own
|
|
@@ -2273,25 +2273,25 @@ async function tickCommand(flags, context) {
|
|
|
2273
2273
|
const replyPass = housekeeping?.replies ?? { read: 0, taken: 0, problem: null };
|
|
2274
2274
|
const stepPass = housekeeping?.steps ?? { ran: 0, problems: [] };
|
|
2275
2275
|
const flowPass = housekeeping?.flows ?? { moved: 0, filed: [], problems: [] };
|
|
2276
|
-
// Each
|
|
2276
|
+
// Each subagent's daily summary to its manager (v92), once, after 5 pm; its weekly report (v97), Monday mornings.
|
|
2277
2277
|
if (!buildsOnly)
|
|
2278
2278
|
try {
|
|
2279
|
-
|
|
2280
|
-
|
|
2279
|
+
sendSubagentSummaries(store, repo, clock());
|
|
2280
|
+
sendSubagentWeeklies(store, repo, clock());
|
|
2281
2281
|
}
|
|
2282
2282
|
catch (error) {
|
|
2283
|
-
flowPass.problems.push(`
|
|
2283
|
+
flowPass.problems.push(`subagent summaries: ${error instanceof Error ? error.message : "could not send"}`);
|
|
2284
2284
|
}
|
|
2285
|
-
// Undoing a
|
|
2285
|
+
// Undoing a subagent's tool call asked for in chat (v97): made here, as the person who asked.
|
|
2286
2286
|
if (!buildsOnly && context.shouldStop?.() !== true) {
|
|
2287
2287
|
try {
|
|
2288
2288
|
await runRequestedUndos(store, {}, clock());
|
|
2289
2289
|
}
|
|
2290
2290
|
catch (error) {
|
|
2291
|
-
flowPass.problems.push(`
|
|
2291
|
+
flowPass.problems.push(`subagent undo: ${error instanceof Error ? error.message : "could not run"}`);
|
|
2292
2292
|
}
|
|
2293
2293
|
}
|
|
2294
|
-
// Tools connected by signing in: a sign-in that runs out within ten minutes is renewed here, so a build or a
|
|
2294
|
+
// Tools connected by signing in: a sign-in that runs out within ten minutes is renewed here, so a build or a subagent's call starts with a fresh one.
|
|
2295
2295
|
if (!buildsOnly && context.shouldStop?.() !== true) {
|
|
2296
2296
|
try {
|
|
2297
2297
|
flowPass.problems.push(...(await refreshConnections(store, [repo], clock(), context.flowStepIo?.fetch === undefined ? {} : { fetcher: context.flowStepIo.fetch })).problems);
|
|
@@ -6050,19 +6050,19 @@ async function runWatchLoop(args) {
|
|
|
6050
6050
|
: startTelegramFollower({ context, publicUrl: text(flags, "public-url"), signal: followController.signal, progress, label: "watch: bridge", died: { prefix: "watch: ", then: "taps wait for the next watch" } });
|
|
6051
6051
|
const slackFollower = followSlack({ store, dir: dirname(context.databaseFile), signal: followController.signal,
|
|
6052
6052
|
readProjects: telegramReadProjects(context), evidenceRoot: context.evidenceRoot,
|
|
6053
|
-
...(context.
|
|
6053
|
+
...(context.leadSeams?.subscriptionRunner ? { subscriptionRunner: context.leadSeams.subscriptionRunner } : {}),
|
|
6054
6054
|
origin: () => phoneOrigin(process.env, dirname(context.databaseFile), { serverOrigin: text(flags, "public-url") ?? null }),
|
|
6055
6055
|
notifications: () => effectivePrimary(process.env, dirname(context.databaseFile), loadBotToken(process.env, context.telegramTokenFile) !== null).channel === "slack",
|
|
6056
6056
|
}).catch(() => progress("watch: Slack stopped. Check Slack settings before reconnecting."));
|
|
6057
6057
|
const teamsFollower = followTeams({ store, dir: dirname(context.databaseFile), signal: followController.signal,
|
|
6058
6058
|
readProjects: telegramReadProjects(context), evidenceRoot: context.evidenceRoot,
|
|
6059
|
-
...(context.
|
|
6059
|
+
...(context.leadSeams?.subscriptionRunner ? { subscriptionRunner: context.leadSeams.subscriptionRunner } : {}),
|
|
6060
6060
|
origin: () => phoneOrigin(process.env, dirname(context.databaseFile), { serverOrigin: text(flags, "public-url") ?? null }),
|
|
6061
6061
|
notifications: () => effectivePrimary(process.env, dirname(context.databaseFile), loadBotToken(process.env, context.telegramTokenFile) !== null).channel === "teams",
|
|
6062
6062
|
}).catch(() => progress("watch: Teams stopped. Check Teams settings before reconnecting."));
|
|
6063
6063
|
const discordFollower = followDiscord({ store, dir: dirname(context.databaseFile), signal: followController.signal,
|
|
6064
6064
|
readProjects: telegramReadProjects(context), evidenceRoot: context.evidenceRoot,
|
|
6065
|
-
...(context.
|
|
6065
|
+
...(context.leadSeams?.subscriptionRunner ? { subscriptionRunner: context.leadSeams.subscriptionRunner } : {}),
|
|
6066
6066
|
origin: () => phoneOrigin(process.env, dirname(context.databaseFile), { serverOrigin: text(flags, "public-url") ?? null }),
|
|
6067
6067
|
notifications: () => effectivePrimary(process.env, dirname(context.databaseFile), loadBotToken(process.env, context.telegramTokenFile) !== null).channel === "discord",
|
|
6068
6068
|
}).catch(() => progress("watch: Discord stopped. Check Discord settings before reconnecting."));
|
|
@@ -9987,7 +9987,7 @@ function describeApproveFailure(reason, id) {
|
|
|
9987
9987
|
if (reason === "requester")
|
|
9988
9988
|
return `you filed ${id}, and this project needs someone else to approve it`;
|
|
9989
9989
|
if (reason === "person-required")
|
|
9990
|
-
return `${id} is protected work: a person has to approve it, not an operating mode, a schedule or
|
|
9990
|
+
return `${id} is protected work: a person has to approve it, not an operating mode, a schedule or a subagent`;
|
|
9991
9991
|
if (reason === "second-approver")
|
|
9992
9992
|
return `your approval of ${id} is recorded; it is protected work, so a second person needs to approve it (toolroll task approve ${id} as them)`;
|
|
9993
9993
|
if (reason === "no-approvers") {
|
|
@@ -10104,7 +10104,7 @@ async function chatCommand(flags, context) {
|
|
|
10104
10104
|
return fail(write, json, "chat", "usage", "Choose --follow or --no-follow.", EXIT.usage);
|
|
10105
10105
|
const credentials = await askCredentials(flags, context);
|
|
10106
10106
|
if (credentials === null) {
|
|
10107
|
-
return fail(write, json, "chat", "usage", "the
|
|
10107
|
+
return fail(write, json, "chat", "usage", "the lead takes your name and password — `--as <you>` and the hidden prompt; `--token <t>` only where a script must (it lands in shell history)", EXIT.usage);
|
|
10108
10108
|
}
|
|
10109
10109
|
const ceilingGiven = text(flags, "ceiling-usd");
|
|
10110
10110
|
// The ceiling: the `--repo` list, or the ENROLLED registry beside the
|
|
@@ -10120,7 +10120,7 @@ async function chatCommand(flags, context) {
|
|
|
10120
10120
|
return fail(write, json, "chat", "registry", "the enrolled-project registry could not be read — name projects with --repo", EXIT.refused);
|
|
10121
10121
|
repos = loaded.repos;
|
|
10122
10122
|
}
|
|
10123
|
-
const result = await
|
|
10123
|
+
const result = await runLeadCli({
|
|
10124
10124
|
store,
|
|
10125
10125
|
databaseFile: context.databaseFile,
|
|
10126
10126
|
write,
|
|
@@ -10131,7 +10131,7 @@ async function chatCommand(flags, context) {
|
|
|
10131
10131
|
end: flags.has("end"),
|
|
10132
10132
|
...(flags.has("follow") || flags.has("no-follow") ? { follow: flags.has("follow") && !flags.has("no-follow") } : {}),
|
|
10133
10133
|
ceilingUsd: ceilingGiven === undefined ? undefined : Number(ceilingGiven),
|
|
10134
|
-
...(context.
|
|
10134
|
+
...(context.leadSeams === undefined ? {} : { seams: { ...context.leadSeams, clock: context.leadSeams.clock ?? context.clock } }),
|
|
10135
10135
|
...(context.evidenceRoot === undefined ? {} : { evidenceRoot: context.evidenceRoot }),
|
|
10136
10136
|
// The console's own address (and its public one): links there read as "the task", "the result", "Settings → Lead".
|
|
10137
10137
|
appOrigin: [loadConsoleUrl(process.env, dirname(context.databaseFile)), phoneOrigin(process.env, dirname(context.databaseFile))],
|
|
@@ -10698,7 +10698,7 @@ async function projectDeleteCommand(positional, flags, context) {
|
|
|
10698
10698
|
`Deleted ${repo}: ${holdingsWords(done.removed)}.`, ...done.left.map(one => `Git kept ${one}.`), `The ledger records it as entry #${done.ledgerId}.`,
|
|
10699
10699
|
]);
|
|
10700
10700
|
}
|
|
10701
|
-
/** `spend [--month YYYY-MM] [--csv]` (v105): what agent work cost in a month, by project, person,
|
|
10701
|
+
/** `spend [--month YYYY-MM] [--csv]` (v105): what agent work cost in a month, by project, person, subagent and model. */
|
|
10702
10702
|
function spendCommand(flags, context) {
|
|
10703
10703
|
const command = "spend";
|
|
10704
10704
|
for (const name of flags.keys())
|
|
@@ -10708,7 +10708,7 @@ function spendCommand(flags, context) {
|
|
|
10708
10708
|
if (month === null)
|
|
10709
10709
|
return fail(context.write, context.json, command, "usage", "Use spend --month YYYY-MM.", EXIT.usage);
|
|
10710
10710
|
const items = spendItems(context.store.handle, month.from, month.to);
|
|
10711
|
-
const names =
|
|
10711
|
+
const names = subagentNames(context.store.handle);
|
|
10712
10712
|
if (flags.has("csv")) {
|
|
10713
10713
|
context.write(spendCsv(items, names).replace(/^\ufeff/, "").trimEnd());
|
|
10714
10714
|
return EXIT.ok;
|
|
@@ -10722,7 +10722,7 @@ function spendCommand(flags, context) {
|
|
|
10722
10722
|
byProject.set(item.project, (byProject.get(item.project) ?? 0) + (item.microusd ?? 0));
|
|
10723
10723
|
// v105: subscription work is $0; what binds it is its plan's windows, as the provider last said.
|
|
10724
10724
|
const limits = context.store.providerLimits();
|
|
10725
|
-
const windows = limitsView(limits, [], { project: repo => basename(repo),
|
|
10725
|
+
const windows = limitsView(limits, [], { project: repo => basename(repo), subagent: id => String(id) }, context.clock())?.tiles ?? [];
|
|
10726
10726
|
// Sized routing: each tier's tasks, time to a result and plan use, and what the tiers saved.
|
|
10727
10727
|
const tiers = tierReport(context.store.handle, month.from, month.to, items);
|
|
10728
10728
|
return succeed(context.write, context.json, command, { month: month.name, totalMicrousd: total, unpriced, items: items.length, budgets, limits, tiers: { rows: tiers.rows, saved: tiers.saved } }, () => [
|
|
@@ -10825,21 +10825,22 @@ async function budgetCommand(positional, flags, context) {
|
|
|
10825
10825
|
const store = context.store;
|
|
10826
10826
|
if (action === undefined || action === "list") {
|
|
10827
10827
|
const budgets = store.monthSpend(context.clock()).budgets;
|
|
10828
|
-
return succeed(context.write, context.json, command, { budgets }, () => budgets.length === 0 ? ["No budgets. `budget set --all|--project <path>|--person <name>|--
|
|
10828
|
+
return succeed(context.write, context.json, command, { budgets }, () => budgets.length === 0 ? ["No budgets. `budget set --all|--project <path>|--person <name>|--subagent <id> --usd <n>` makes one."]
|
|
10829
10829
|
: budgets.map(one => `#${one.id} ${budgetLabel(one).replace(/'s$/, "")}: ${spendUsd(one.spentMicrousd)} of ${spendUsd(one.limitMicrousd)} this month (${one.percent}%)${one.hardStop ? ", stops API work at 100%" : ", alerts only"}`));
|
|
10830
10830
|
}
|
|
10831
10831
|
if (action !== "set" && action !== "remove")
|
|
10832
10832
|
return fail(context.write, context.json, command, "usage", "Use budget list, budget set or budget remove.", EXIT.usage);
|
|
10833
|
-
|
|
10833
|
+
// --teammate: the flag's name before D5, still read this release.
|
|
10834
|
+
const allowed = new Set(["all", "project", "person", "subagent", "teammate", "usd", "alerts-only", "as", "token", "token-file", "token-env", "db", "json"]);
|
|
10834
10835
|
for (const name of flags.keys())
|
|
10835
10836
|
if (!allowed.has(name))
|
|
10836
10837
|
return fail(context.write, context.json, command, "usage", `--${name} is not a budget option.`, EXIT.usage);
|
|
10837
10838
|
const chosen = [flags.has("all") ? { scope: "installation", key: "*" } : null,
|
|
10838
10839
|
text(flags, "project") === undefined ? null : { scope: "project", key: resolve(text(flags, "project")) },
|
|
10839
10840
|
text(flags, "person") === undefined ? null : { scope: "person", key: text(flags, "person") },
|
|
10840
|
-
text(flags, "teammate") === undefined ? null : { scope: "
|
|
10841
|
+
(text(flags, "subagent") ?? text(flags, "teammate")) === undefined ? null : { scope: "subagent", key: (text(flags, "subagent") ?? text(flags, "teammate")) }].filter(one => one !== null);
|
|
10841
10842
|
if (chosen.length !== 1)
|
|
10842
|
-
return fail(context.write, context.json, command, "usage", "Name one of --all, --project <path>, --person <name> or --
|
|
10843
|
+
return fail(context.write, context.json, command, "usage", "Name one of --all, --project <path>, --person <name> or --subagent <id>.", EXIT.usage);
|
|
10843
10844
|
const target = chosen[0];
|
|
10844
10845
|
const acting = await askCredentials(flags, context);
|
|
10845
10846
|
const verified = acting === null ? null : authenticateApprover(store, acting.name, acting.token);
|
|
@@ -10848,9 +10849,9 @@ async function budgetCommand(positional, flags, context) {
|
|
|
10848
10849
|
}
|
|
10849
10850
|
const known = target.scope === "installation" || (target.scope === "project" ? store.knownRepos().includes(target.key)
|
|
10850
10851
|
: target.scope === "person" ? store.accountFacts().some(one => one.name === target.key && one.revokedAt === null)
|
|
10851
|
-
: store.handle.prepare("SELECT 1 FROM
|
|
10852
|
+
: store.handle.prepare("SELECT 1 FROM subagent WHERE id = ?").get(Number(target.key)) !== undefined);
|
|
10852
10853
|
if (!known)
|
|
10853
|
-
return fail(context.write, context.json, command, "not-found", "That isn't a project, person or
|
|
10854
|
+
return fail(context.write, context.json, command, "not-found", "That isn't a project, person or subagent here.", EXIT.refused);
|
|
10854
10855
|
if (action === "remove") {
|
|
10855
10856
|
const existing = store.budgets().find(one => one.scope === target.scope && one.key === target.key);
|
|
10856
10857
|
if (existing === undefined || !store.removeBudget(existing.id, acting.name, context.clock()))
|
package/dist/policy.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* (lowered), or, when there is none, doesn't run (refused); the words say which.
|
|
16
16
|
*
|
|
17
17
|
* Every road that admits work asks here: scope approval, the tick before a
|
|
18
|
-
* run starts (and build() as the last look), the lead and project chats,
|
|
18
|
+
* run starts (and build() as the last look), the lead and project chats, subagents and flow steps.
|
|
19
19
|
*/
|
|
20
20
|
import type { Database } from "./store.js";
|
|
21
21
|
import type { ExecutionProfile } from "./scope.js";
|
package/dist/principal.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type VerifiedApprover = {
|
|
|
9
9
|
readonly repos: readonly string[];
|
|
10
10
|
readonly ceilingDigest: string;
|
|
11
11
|
};
|
|
12
|
-
/** The
|
|
12
|
+
/** The lead's ceiling digest: the admitted repos IN ORDER, hashed — `rN`
|
|
13
13
|
* is an index, so two surfaces naming the same repos in a different order
|
|
14
14
|
* hold different ceilings (slice-2 review, finding 3). */
|
|
15
15
|
export declare function ceilingDigestOf(repos: readonly string[]): string;
|
package/dist/principal.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The branded approver principal (mate arc, ruling 3).
|
|
3
3
|
*
|
|
4
|
-
* Every act the
|
|
4
|
+
* Every act the lead proposes and every confirmation an operator taps
|
|
5
5
|
* runs under a principal that PROVES it was authenticated — a password
|
|
6
6
|
* ceremony, or a cookie session the edge already proved (csrf, role,
|
|
7
7
|
* generation) and this module re-proves against the approver row. The
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { createHash } from "node:crypto";
|
|
17
17
|
import { authenticateAccount } from "./scope.js";
|
|
18
|
-
/** The
|
|
18
|
+
/** The lead's ceiling digest: the admitted repos IN ORDER, hashed — `rN`
|
|
19
19
|
* is an index, so two surfaces naming the same repos in a different order
|
|
20
20
|
* hold different ceilings (slice-2 review, finding 3). */
|
|
21
21
|
export function ceilingDigestOf(repos) {
|
|
@@ -49,7 +49,7 @@ export function projectDeleteConfirmHtml(view, problem) {
|
|
|
49
49
|
"The checkouts and branches Toolroll made",
|
|
50
50
|
...(h.chats > 0 ? [count(h.chats, "chat")] : []),
|
|
51
51
|
...(h.flows > 0 ? [`${count(h.flows, "flow")} and ${count(h.cards, "card")}`] : []),
|
|
52
|
-
...(h.
|
|
52
|
+
...(h.subagents > 0 ? [count(h.subagents, "subagent")] : []),
|
|
53
53
|
"Its budgets, settings and knowledge",
|
|
54
54
|
];
|
|
55
55
|
return html `<section class="project-delete">${problem === null ? "" : html `<p class="problem" role="alert">${problem}</p>`}<p>This removes:</p><ul class="removes">${items.map(one => html `<li>${one}</li>`)}</ul><p>The repository at <span class="path">${view.repo}</span> and its own branches stay. The ledger keeps its history. There's no undo.</p>${postForm("/settings/project/delete", html `<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label><div class="actions"><button type="submit" class="danger">Delete project</button><a href="/settings/project?repo=${encodeURIComponent(view.repo)}">Cancel</a></div>`, { hidden: { repo: view.repo, name: view.name, step: "delete" } })}</section>`;
|
package/dist/project-delete.d.ts
CHANGED
package/dist/project-delete.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Deleting a project: everything Toolroll holds for it goes, and
|
|
3
3
|
* nothing else. Its tasks and their versions, runs and their evidence, the
|
|
4
4
|
* checkouts and branches Toolroll made, its chats and threads, flows
|
|
5
|
-
* and cards,
|
|
5
|
+
* and cards, subagents, budgets and settings. Never while any of its work is
|
|
6
6
|
* running. The project's own repository, its working copy and every branch
|
|
7
7
|
* Toolroll didn't make stay exactly as they are. The action ledger
|
|
8
8
|
* keeps every entry (its hash chain still verifies) and gains one: who
|
|
@@ -36,13 +36,13 @@ function doomed(db, repo) {
|
|
|
36
36
|
const runs = ids(db, "SELECT id FROM run WHERE task_ref IN (SELECT value FROM json_each(?))", json(refs));
|
|
37
37
|
const artifacts = ids(db, "SELECT id FROM artifact WHERE run IN (SELECT value FROM json_each(?))", json(runs));
|
|
38
38
|
// A team conversation's thread is the team's, whatever it talked about.
|
|
39
|
-
const threads = ids(db, `SELECT id FROM
|
|
39
|
+
const threads = ids(db, `SELECT id FROM lead_thread WHERE ((scope_kind = 'project' AND scope_key = ?) OR (scope_kind = 'task' AND scope_key IN (SELECT value FROM json_each(?))))
|
|
40
40
|
AND id NOT IN (SELECT thread FROM team_conversation WHERE thread IS NOT NULL)`, repo, json(tasks));
|
|
41
41
|
const turns = ids(db, "SELECT id FROM mate_turn WHERE thread IN (SELECT value FROM json_each(?))", json(threads));
|
|
42
42
|
const flows = ids(db, "SELECT id FROM flow WHERE repo = ?", repo);
|
|
43
43
|
const cards = ids(db, "SELECT id FROM flow_card WHERE flow IN (SELECT value FROM json_each(?))", json(flows));
|
|
44
|
-
const
|
|
45
|
-
return { refs, tasks, runs, artifacts, threads, turns, flows, cards,
|
|
44
|
+
const subagents = ids(db, "SELECT id FROM subagent WHERE repo = ?", repo);
|
|
45
|
+
return { refs, tasks, runs, artifacts, threads, turns, flows, cards, subagents };
|
|
46
46
|
}
|
|
47
47
|
/** A project's settings: one row per thing someone set for it. */
|
|
48
48
|
const SETTINGS = [
|
|
@@ -61,8 +61,8 @@ export function projectHoldings(store, repo) {
|
|
|
61
61
|
tasks: count("SELECT COUNT(DISTINCT external_id) AS n FROM task_ref WHERE repo = ?", repo) - versions, versions,
|
|
62
62
|
runs: d.runs.length, evidence: d.artifacts.length,
|
|
63
63
|
checkouts: count("SELECT COUNT(*) AS n FROM worktree WHERE repo = ?", repo),
|
|
64
|
-
chats: d.threads.length, flows: d.flows.length, cards: d.cards.length,
|
|
65
|
-
budgets: count(`SELECT COUNT(*) AS n FROM budget WHERE removed_at IS NULL AND ((scope_kind = 'project' AND scope_key = ?) OR (scope_kind = '
|
|
64
|
+
chats: d.threads.length, flows: d.flows.length, cards: d.cards.length, subagents: d.subagents.length,
|
|
65
|
+
budgets: count(`SELECT COUNT(*) AS n FROM budget WHERE removed_at IS NULL AND ((scope_kind = 'project' AND scope_key = ?) OR (scope_kind = 'subagent' AND scope_key IN (SELECT CAST(value AS TEXT) FROM json_each(?))))`, repo, json(d.subagents)),
|
|
66
66
|
settings: SETTINGS.filter(([table]) => tableExists(db, table)).reduce((sum, [table, column]) => sum + count(`SELECT COUNT(*) AS n FROM ${table} WHERE ${column} = ?`, repo), 0),
|
|
67
67
|
};
|
|
68
68
|
}
|
|
@@ -72,7 +72,7 @@ export function holdingsWords(held) {
|
|
|
72
72
|
plural(held.tasks, "task"), ...(held.versions > 0 ? [plural(held.versions, "version")] : []), plural(held.runs, "run"),
|
|
73
73
|
...(held.evidence > 0 ? [plural(held.evidence, "evidence file")] : []), ...(held.checkouts > 0 ? [plural(held.checkouts, "checkout")] : []),
|
|
74
74
|
...(held.branches ? [plural(held.branches, "branch", "branches")] : []), ...(held.chats > 0 ? [plural(held.chats, "chat")] : []),
|
|
75
|
-
...(held.flows > 0 ? [`${plural(held.flows, "flow")} with ${plural(held.cards, "card")}`] : []), ...(held.
|
|
75
|
+
...(held.flows > 0 ? [`${plural(held.flows, "flow")} with ${plural(held.cards, "card")}`] : []), ...(held.subagents > 0 ? [plural(held.subagents, "subagent")] : []),
|
|
76
76
|
...(held.budgets > 0 ? [plural(held.budgets, "budget")] : []), ...(held.settings > 0 ? [plural(held.settings, "setting")] : []),
|
|
77
77
|
];
|
|
78
78
|
return parts.join(", ");
|
|
@@ -87,7 +87,7 @@ export function projectRunning(store, repo, now) {
|
|
|
87
87
|
const slots = count("SELECT COUNT(*) AS n FROM execution_slot WHERE state IN ('reserved','running') AND run IN (SELECT value FROM json_each(?))", json(d.runs));
|
|
88
88
|
const chats = count("SELECT COUNT(*) AS n FROM mate_turn WHERE state IN ('queued','running') AND id IN (SELECT value FROM json_each(?))", json(d.turns));
|
|
89
89
|
const steps = count("SELECT COUNT(*) AS n FROM flow_step_run WHERE state = 'running' AND card IN (SELECT value FROM json_each(?))", json(d.cards));
|
|
90
|
-
const calls = count("SELECT COUNT(*) AS n FROM
|
|
90
|
+
const calls = count("SELECT COUNT(*) AS n FROM subagent_call WHERE state = 'running' AND subagent IN (SELECT value FROM json_each(?))", json(d.subagents));
|
|
91
91
|
const merges = count(`SELECT COUNT(*) AS n FROM merge_intent WHERE state IN ('claimed','firing') AND publication IN (SELECT id FROM publication WHERE task_ref IN (SELECT value FROM json_each(?)))`, json(d.refs));
|
|
92
92
|
const checkouts = count("SELECT COUNT(*) AS n FROM worktree WHERE repo = ? AND runner IS NOT NULL AND released_at IS NULL", repo);
|
|
93
93
|
return [
|
|
@@ -95,7 +95,7 @@ export function projectRunning(store, repo, now) {
|
|
|
95
95
|
...(slots > 0 ? ["an agent is working"] : []),
|
|
96
96
|
...(chats > 0 ? ["a chat is answering"] : []),
|
|
97
97
|
...(steps > 0 ? ["a flow step is running"] : []),
|
|
98
|
-
...(calls > 0 ? ["a
|
|
98
|
+
...(calls > 0 ? ["a subagent is working"] : []),
|
|
99
99
|
...(merges > 0 ? ["a merge is in progress"] : []),
|
|
100
100
|
...(checkouts > 0 && building === 0 ? [`${plural(checkouts, "checkout")} ${checkouts === 1 ? "is" : "are"} in use`] : []),
|
|
101
101
|
];
|
|
@@ -201,12 +201,12 @@ function deleteRows(store, repo, d, now) {
|
|
|
201
201
|
rows += Number(db.prepare(`DELETE FROM ${table} WHERE ${where}`).run(...params).changes);
|
|
202
202
|
};
|
|
203
203
|
const IN = (column) => `${column} IN (SELECT value FROM json_each(?))`;
|
|
204
|
-
const R = json(d.runs), T = json(d.refs), K = json(d.tasks), A = json(d.artifacts), H = json(d.threads), U = json(d.turns), F = json(d.flows), C = json(d.cards), M = json(d.
|
|
204
|
+
const R = json(d.runs), T = json(d.refs), K = json(d.tasks), A = json(d.artifacts), H = json(d.threads), U = json(d.turns), F = json(d.flows), C = json(d.cards), M = json(d.subagents);
|
|
205
205
|
const decisions = json(ids(db, `SELECT id FROM decision WHERE ${IN("run")}`, R));
|
|
206
206
|
const publications = json(ids(db, `SELECT id FROM publication WHERE ${IN("task_ref")} OR ${IN("run")}`, T, R));
|
|
207
207
|
const notifications = json(ids(db, `SELECT id FROM notification WHERE project = ? OR ${IN("task_ref")} OR ${IN("source_run")}`, repo, T, R));
|
|
208
|
-
const questions = json(ids(db, `SELECT id FROM
|
|
209
|
-
const proposals = json(ids(db, `SELECT id FROM
|
|
208
|
+
const questions = json(ids(db, `SELECT id FROM subagent_question WHERE ${IN("subagent")} OR ${IN("card")}`, M, C));
|
|
209
|
+
const proposals = json(ids(db, `SELECT id FROM lead_proposal WHERE ${IN("thread")}`, H));
|
|
210
210
|
// A chat message about one of the project's tasks (its turn named it), with its reply. Event ids are text ('m123'),
|
|
211
211
|
// and the same id can be another app's: each is kept whole, as provider:id.
|
|
212
212
|
const conversations = json(list(db, `SELECT provider || ':' || id FROM chat_event WHERE kind = 'message' AND json_extract(payload, '$.about.task') IN (SELECT value FROM json_each(?))`, K));
|
|
@@ -241,13 +241,13 @@ function deleteRows(store, repo, d, now) {
|
|
|
241
241
|
del("chat_focus", IN("task"), K);
|
|
242
242
|
del("team_task_owner", IN("task_ref"), T);
|
|
243
243
|
del("team_mate_session", IN("thread"), H);
|
|
244
|
-
del("
|
|
244
|
+
del("lead_turn_evidence", `${IN("turn")} OR ${IN("run")} OR ${IN("task_ref")} OR ${IN("artifact")}`, U, R, T, A);
|
|
245
245
|
del("chat_turn", IN("mate_turn"), U);
|
|
246
|
-
del("
|
|
246
|
+
del("lead_ask", IN("turn"), U);
|
|
247
247
|
del("mate_turn", IN("id"), U);
|
|
248
|
-
del("
|
|
249
|
-
del("
|
|
250
|
-
del("
|
|
248
|
+
del("lead_proposal", IN("id"), proposals);
|
|
249
|
+
del("lead_message", IN("thread"), H);
|
|
250
|
+
del("lead_thread", IN("id"), H);
|
|
251
251
|
// Runs and their evidence.
|
|
252
252
|
del("run_decision", `${IN("run")} OR ${IN("decision")}`, R, decisions);
|
|
253
253
|
del("decision_artifact", `${IN("decision")} OR ${IN("artifact")}`, decisions, A);
|
|
@@ -288,19 +288,19 @@ function deleteRows(store, repo, d, now) {
|
|
|
288
288
|
del("task_scope", IN("task_id"), K);
|
|
289
289
|
del("task_edge", `${IN("blocked")} OR ${IN("blocker")}`, K, K);
|
|
290
290
|
del("task", IN("id"), K);
|
|
291
|
-
// Flows, cards and
|
|
291
|
+
// Flows, cards and subagents.
|
|
292
292
|
del("flow_trigger_event", `${IN("card")} OR trigger IN (SELECT id FROM flow_trigger WHERE ${IN("flow")})`, C, F);
|
|
293
293
|
del("flow_trigger", IN("flow"), F);
|
|
294
294
|
for (const table of ["flow_card_watcher", "flow_comment", "flow_event", "flow_mail", "flow_step_run"])
|
|
295
295
|
del(table, IN("card"), C);
|
|
296
|
-
del("
|
|
297
|
-
del("
|
|
298
|
-
for (const table of ["
|
|
299
|
-
del(table, `${IN("
|
|
300
|
-
for (const table of ["
|
|
301
|
-
del(table, IN("
|
|
302
|
-
del("budget", `(scope_kind = 'project' AND scope_key = ?) OR (scope_kind = '
|
|
303
|
-
del("
|
|
296
|
+
del("subagent_question", IN("id"), questions);
|
|
297
|
+
del("subagent_call", `${IN("subagent")} OR ${IN("card")}`, M, C);
|
|
298
|
+
for (const table of ["subagent_event", "subagent_memory", "subagent_turn"])
|
|
299
|
+
del(table, `${IN("subagent")} OR ${IN("card")}`, M, C);
|
|
300
|
+
for (const table of ["subagent_suggestion", "subagent_tool", "subagent_version"])
|
|
301
|
+
del(table, IN("subagent"), M);
|
|
302
|
+
del("budget", `(scope_kind = 'project' AND scope_key = ?) OR (scope_kind = 'subagent' AND scope_key IN (SELECT CAST(value AS TEXT) FROM json_each(?)))`, repo, M);
|
|
303
|
+
del("subagent", IN("id"), M);
|
|
304
304
|
del("flow_card", IN("id"), C);
|
|
305
305
|
del("flow", IN("id"), F);
|
|
306
306
|
// Settings, knowledge and the project itself.
|
package/dist/project-memory.js
CHANGED
|
@@ -222,7 +222,7 @@ export function reindexProjectMemory(store, repo) {
|
|
|
222
222
|
export function reindexConversations(store) {
|
|
223
223
|
const key = 'memory-search:messages';
|
|
224
224
|
const cursor = store.serviceCursor(key);
|
|
225
|
-
const rows = store.handle.prepare('SELECT m.id,m.thread,m.role,m.text,t.approver FROM
|
|
225
|
+
const rows = store.handle.prepare('SELECT m.id,m.thread,m.role,m.text,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id>? ORDER BY m.id LIMIT 500').all(cursor);
|
|
226
226
|
if (rows.length === 0)
|
|
227
227
|
return;
|
|
228
228
|
const insert = store.handle.prepare('INSERT INTO memory_search(kind,ref,repo,scope,title,body) VALUES (?,?,?,?,?,?)');
|