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/project-tools.d.ts
CHANGED
|
@@ -91,7 +91,7 @@ export declare function testTool(spec: ToolSpec, values: Record<string, string>,
|
|
|
91
91
|
/**
|
|
92
92
|
* Start the server once and read what each of its tools does (v94): the name,
|
|
93
93
|
* its description, what input it takes, and whether it says it only reads.
|
|
94
|
-
* A
|
|
94
|
+
* A subagent is offered these, each under its own rule.
|
|
95
95
|
*/
|
|
96
96
|
export declare function listProjectToolActions(spec: ToolSpec, values: Record<string, string>, options?: {
|
|
97
97
|
timeoutMs?: number;
|
package/dist/project-tools.js
CHANGED
|
@@ -382,7 +382,7 @@ export async function testTool(spec, values, options = {}) {
|
|
|
382
382
|
/**
|
|
383
383
|
* Start the server once and read what each of its tools does (v94): the name,
|
|
384
384
|
* its description, what input it takes, and whether it says it only reads.
|
|
385
|
-
* A
|
|
385
|
+
* A subagent is offered these, each under its own rule.
|
|
386
386
|
*/
|
|
387
387
|
export async function listProjectToolActions(spec, values, options = {}) {
|
|
388
388
|
const answer = await exchange(spec, values, { method: "tools/list", params: {}, doing: "listing its tools" }, options);
|
package/dist/recipe-ui.js
CHANGED
|
@@ -55,7 +55,7 @@ export function recipeFromForm(body) {
|
|
|
55
55
|
}
|
|
56
56
|
export function recipeLibraryHtml(starters, saved, project, revision, recent) {
|
|
57
57
|
const cards = (recipes, reusable = false) => html `<div class="recipe-grid"${reusable && html ` data-saved-recipes`}>${recipes.map(recipe => html `<article class="recipe-card" data-recipe-card data-recipe-search="${recipe.document.name + " " + recipe.document.description}"><p class="recipe-kicker">${recipe.document.deliverable === "report" ? "Report" : recipe.document.schedule === null ? "One-time work" : "Recurring care"}${recipe.document.version === 2 ? ` · ${recipe.document.inputs.length} question${recipe.document.inputs.length === 1 ? "" : "s"}` : ""}</p><h3>${recipe.document.name}</h3><p>${recipe.document.description}</p><a class="recipe-card-link" href="/recipes/${reusable ? "run" : "start"}?recipe=${recipe.id}&revision=${recipe.revision}">${reusable ? "Use recipe" : "Customize recipe"} <span aria-hidden="true">→</span></a>${reusable && html `<a class="meta" href="/recipes/start?recipe=${recipe.id}&revision=${recipe.revision}">Edit a copy</a>`}</article>`)}</div>`;
|
|
58
|
-
const savedSection = html `<h2>Your project’s recipes</h2>${saved.length >= 100 && html `<p class="meta">Showing 100 recipes, with recently used recipes first.</p>`}${saved.length ? html `<p class="hint">Your usual work, ready to go. Recently used recipes come first.</p><label class="recipe-search">Find a recipe<input type="search" data-recipe-search-input placeholder="Search your recipes…"></label>${cards(saved, true)}<p data-recipe-empty hidden>No recipes match. Try another search.</p>` : html `<p class="hint">Save work you do often.
|
|
58
|
+
const savedSection = html `<h2>Your project’s recipes</h2>${saved.length >= 100 && html `<p class="meta">Showing 100 recipes, with recently used recipes first.</p>`}${saved.length ? html `<p class="hint">Your usual work, ready to go. Recently used recipes come first.</p><label class="recipe-search">Find a recipe<input type="search" data-recipe-search-input placeholder="Search your recipes…"></label>${cards(saved, true)}<p data-recipe-empty hidden>No recipes match. Try another search.</p>` : html `<p class="hint">Save work you do often. Subagents with project access can use the same recipe.</p>`}`;
|
|
59
59
|
return html `<div class="recipe-heading"><p class="recipe-kicker">Workflows</p><h1>Good work, on repeat.</h1><p class="hint">Save the work you do often. Answer what changes this time. Kick it off.</p><p><a href="/recipes/new" class="new-task">Create a recipe</a> <a href="/flows">Manage scheduled work →</a></p></div>${project === null && html `<div class="card"><strong>Choose a project to get started.</strong><p>Browse recipes now; <a href="/projects">open a project</a> to customize, save, or run one.</p></div>`}${saved.length > 0 && savedSection}<h2>Start here</h2>${cards(starters)}${saved.length === 0 && savedSection}${recent.length > 0 && html `<h2>Recent workflows</h2><div class="card recipe-history">${recent.map(one => html `<a href="${one.href}"><strong>${one.name}</strong><span>${one.state} →</span></a>`)}</div>`}${project !== null && html `<details class="card"><summary>Import a shared recipe</summary><p class="hint">Paste exported recipe JSON. You’ll review it in this project before creating any work.</p>${postForm("/recipes/import", html `<label>Recipe JSON<textarea name="document" rows="7" required maxlength="32768" spellcheck="false"></textarea></label><button type="submit">Review imported recipe</button>`, { projectRevision: String(revision), hidden: { repo: project } })}</details>`}`;
|
|
60
60
|
}
|
|
61
61
|
function criterionHtml(index, criterion, values) {
|
package/dist/retention.js
CHANGED
|
@@ -174,7 +174,7 @@ export function retentionPlan(store, evidenceRoot, now, periods = store.retentio
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
if (periods.chat !== null) {
|
|
177
|
-
const rows = db.prepare(`SELECT m.id AS id, length(m.text) + COALESCE(length(m.activity), 0) + 64 AS bytes FROM
|
|
177
|
+
const rows = db.prepare(`SELECT m.id AS id, length(m.text) + COALESCE(length(m.activity), 0) + 64 AS bytes FROM lead_message m JOIN lead_thread th ON th.id = m.thread
|
|
178
178
|
WHERE m.created_at < ?
|
|
179
179
|
AND NOT EXISTS (SELECT 1 FROM team_message tm WHERE tm.message = m.id AND tm.status IN ('queued', 'running', 'uncertain'))
|
|
180
180
|
AND NOT EXISTS (SELECT 1 FROM mate_turn turn WHERE turn.id = m.turn AND turn.state IN ('queued', 'running'))
|
|
@@ -263,10 +263,10 @@ export function sweepRetention(store, evidenceRoot, now, actor = "worker") {
|
|
|
263
263
|
}
|
|
264
264
|
if (periods.chat !== null) {
|
|
265
265
|
const team = db.prepare("DELETE FROM team_message WHERE message = ? AND status NOT IN ('queued', 'running', 'uncertain')");
|
|
266
|
-
const message = db.prepare("DELETE FROM
|
|
267
|
-
const asked = db.prepare("SELECT turn FROM
|
|
266
|
+
const message = db.prepare("DELETE FROM lead_message WHERE id = ? AND NOT EXISTS (SELECT 1 FROM team_message WHERE message = ?)");
|
|
267
|
+
const asked = db.prepare("SELECT turn FROM lead_message WHERE id = ? AND role = 'assistant' AND turn IS NOT NULL");
|
|
268
268
|
// The lead's question goes with the reply that asked it; its buttons then say it expired.
|
|
269
|
-
const ask = db.prepare("DELETE FROM
|
|
269
|
+
const ask = db.prepare("DELETE FROM lead_ask WHERE turn = ? AND NOT EXISTS (SELECT 1 FROM lead_message WHERE turn = ? AND role = 'assistant')");
|
|
270
270
|
for (const one of plan.items.chat) {
|
|
271
271
|
team.run(one.id);
|
|
272
272
|
const turn = asked.get(one.id)?.["turn"];
|
package/dist/scope.d.ts
CHANGED
|
@@ -304,10 +304,10 @@ export type Scope = SavedScopeTerms & {
|
|
|
304
304
|
* non-null verdict, so filtering, defaulting, or coercion can never
|
|
305
305
|
* turn a corrupt row into authority. Undefined on a hand-built scope. */
|
|
306
306
|
termsProblem?: string | null;
|
|
307
|
-
/** Who wrote the text (mate arc, ruling 2): a confirmed
|
|
307
|
+
/** Who wrote the text (mate arc, ruling 2): a confirmed lead proposal,
|
|
308
308
|
* the coordinator, a scout, or a person (null). Read raw and proved by
|
|
309
309
|
* `termsProblem` — a word outside these is a stated problem, never a
|
|
310
|
-
* value that slips past the
|
|
310
|
+
* value that slips past the lead quarantine at a mode seal. */
|
|
311
311
|
proposedVia?: "mate" | "coordinator" | "scout" | null;
|
|
312
312
|
};
|
|
313
313
|
export type Approval = {
|
package/dist/scout-report.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The scout's terminal handoff (mate arc §10), read against the report contract (src/contracts/scout-report.ts) with
|
|
3
3
|
* the 422 rule: fail closed, every problem reported at once, each naming its path (`items[0].url: must be an http or
|
|
4
4
|
* https address`), stable reasons, caps and control-character rejection on every string. A report reaches the task
|
|
5
|
-
* page, the ledger, the terminal, and — through
|
|
5
|
+
* page, the ledger, the terminal, and — through leadView — the lead; and each follow-up becomes a filing's title and
|
|
6
6
|
* goal at one tap, so it gets the park discipline exactly as the plan does.
|
|
7
7
|
*/
|
|
8
8
|
import { type ContractProblem } from "./contracts/contract.js";
|
package/dist/scout-report.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The scout's terminal handoff (mate arc §10), read against the report contract (src/contracts/scout-report.ts) with
|
|
3
3
|
* the 422 rule: fail closed, every problem reported at once, each naming its path (`items[0].url: must be an http or
|
|
4
4
|
* https address`), stable reasons, caps and control-character rejection on every string. A report reaches the task
|
|
5
|
-
* page, the ledger, the terminal, and — through
|
|
5
|
+
* page, the ledger, the terminal, and — through leadView — the lead; and each follow-up becomes a filing's title and
|
|
6
6
|
* goal at one tap, so it gets the park discipline exactly as the plan does.
|
|
7
7
|
*/
|
|
8
8
|
import { contractProblemOf } from "./contracts/contract.js";
|
package/dist/serve.js
CHANGED
|
@@ -87,7 +87,7 @@ import { firstRunSteps, signInCommandFor } from "./first-run.js";
|
|
|
87
87
|
import { installMethod } from "./install-method.js";
|
|
88
88
|
import { createIntegrationMonitor } from "./integrations.js";
|
|
89
89
|
import { logEvent } from "./log.js";
|
|
90
|
-
import {
|
|
90
|
+
import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
|
|
91
91
|
import {} from "./model-catalog.js";
|
|
92
92
|
import { modeTermsFromJson } from "./modes.js";
|
|
93
93
|
import { envValue } from "./names.js";
|
|
@@ -120,7 +120,7 @@ import { createSettingsHandlers } from './server/settings.js';
|
|
|
120
120
|
import { requestContext } from "./server/request-context.js";
|
|
121
121
|
import { chromeScript, DEMO_BANNER, DEMO_BANNER_SHORT, focusDocument, KBD_HELP, pinnedTheme, QUEUE_VIEW, refuse, screen, SENSITIVE_INPUT, shell, sidebarScript } from "./server/chrome.js";
|
|
122
122
|
import { BODY_CAP, form, loginHref, matchTaskPath, page, projectChatHref, redactedPath, redirect, respond, safeReturn, SHUTDOWN_WAIT_MS, taskChatHref } from "./server/http.js";
|
|
123
|
-
import { decisionsFor, LEAD_BY_DEFAULT_FACT,
|
|
123
|
+
import { decisionsFor, LEAD_BY_DEFAULT_FACT, leadBrowserMessages, leadChatVersion, teamProposalCardParts } from "./server/render-chat.js";
|
|
124
124
|
import {} from "./server/render-pages.js";
|
|
125
125
|
import { ssoStepUps, wrongHostPage } from "./server/render-people.js";
|
|
126
126
|
import { NO_PROJECT, NO_TOUCH_FRAGMENTS, NONCE_CAP, NONCE_TTL_MS, PersistentSessions, SESSION_ABSOLUTE_MS, SESSION_IDLE_MS, SIGN_IN_LINK_MS, SIGN_IN_LINK_PATH } from "./server/session.js";
|
|
@@ -453,7 +453,7 @@ export function createDecisionServer(options) {
|
|
|
453
453
|
if (!snapshot?.selected || !snapshot.proposals)
|
|
454
454
|
return reply;
|
|
455
455
|
return { ...reply, snapshot: { ...snapshot, proposals: snapshot.proposals.map(summary => {
|
|
456
|
-
const proposal = store.
|
|
456
|
+
const proposal = store.getLeadProposal(summary.id);
|
|
457
457
|
if (!proposal || proposal.thread !== snapshot.selected.threadId)
|
|
458
458
|
return summary;
|
|
459
459
|
const decision = proposal.kind === 'answer' && typeof proposal.payload['decision'] === 'number' ? store.getDecision(proposal.payload['decision']) : null;
|
|
@@ -780,10 +780,10 @@ export function createDecisionServer(options) {
|
|
|
780
780
|
}
|
|
781
781
|
const shared = /^\/chat\/(?:action\/([0-9]{1,15})|proposal\/([0-9]{1,15})\/(?:confirm|dismiss))$/.exec(url.pathname);
|
|
782
782
|
if (shared) {
|
|
783
|
-
const proposal = store.
|
|
783
|
+
const proposal = store.getLeadProposal(Number(shared[1] ?? shared[2]));
|
|
784
784
|
const action = proposal?.kind === 'action' ? sharedActionPayload(proposal.payload) : null;
|
|
785
785
|
const room = proposal ? store.handle.prepare('SELECT id FROM team_conversation WHERE thread=?').get(proposal.thread) : null;
|
|
786
|
-
let admitted = proposal && store.
|
|
786
|
+
let admitted = proposal && store.getLeadThread(proposal.thread)?.approver === who.name;
|
|
787
787
|
if (room && who.via === 'cookie') {
|
|
788
788
|
try {
|
|
789
789
|
team.domain.access({ name: who.name, generation: who.session.generation }, String(room['id']), 'contributor');
|
|
@@ -981,36 +981,36 @@ export function createDecisionServer(options) {
|
|
|
981
981
|
// ---- fleet chat (v13) ----------------------------------------------------
|
|
982
982
|
const chatFetcher = options.chatFetcher ?? fetch;
|
|
983
983
|
const chatEnv = options.chatEnv ?? process.env;
|
|
984
|
-
/** The
|
|
984
|
+
/** The lead's last non-answer per BROWSER session (keyed by its csrf —
|
|
985
985
|
* never by name, so two browsers on one account do not read each other's
|
|
986
986
|
* notes), bound to the mate turn it came from so a note from a session
|
|
987
987
|
* that has since ended never shows; bounded; read once. */
|
|
988
|
-
const
|
|
988
|
+
const leadSaid = new Map();
|
|
989
989
|
/** The unified conversation's rows for one reader (package 2): the SAME
|
|
990
990
|
* rows the page renders and the status poll's fragments re-render, so a
|
|
991
991
|
* live update can never show a card the page would not. A task lens
|
|
992
|
-
* keeps only that task's coordinator cards; the
|
|
992
|
+
* keeps only that task's coordinator cards; the lead's own thread is one
|
|
993
993
|
* thread regardless of lens. */
|
|
994
|
-
function
|
|
994
|
+
function leadConversationRows(who, principal, focusTask, now, chatProject = null) {
|
|
995
995
|
const allCoordinatorRows = store.listCoordinatorProposals({ repos: managedRepos(), states: ["pending", "confirmed", "refused"], limit: 30 });
|
|
996
996
|
const coordinatorProposals = focusTask !== null ? allCoordinatorRows.filter(one => focusTask.family.versions.some(version => version.id === one.payload["task"]))
|
|
997
997
|
: chatProject !== null ? allCoordinatorRows.filter(one => one.repo === chatProject) : allCoordinatorRows;
|
|
998
|
-
const opened = store.
|
|
999
|
-
const proposals = store.
|
|
998
|
+
const opened = store.openLeadThread(who.name, principal.ceilingDigest, now, chatScopeOf(focusTask, chatProject));
|
|
999
|
+
const proposals = store.listLeadProposals(opened.thread.id);
|
|
1000
1000
|
// One reply runs at a time per person; this thread shows it only when
|
|
1001
1001
|
// the reply is its own.
|
|
1002
|
-
const live = store.
|
|
1003
|
-
const messages = store.
|
|
1002
|
+
const live = store.liveLeadTurnFor(who.name);
|
|
1003
|
+
const messages = store.listLeadMessages(opened.thread.id, 40);
|
|
1004
1004
|
// The lead's question with buttons, while its reply is the last word in the thread.
|
|
1005
1005
|
const last = messages.at(-1);
|
|
1006
|
-
const ask = last?.role === "assistant" && last.turn !== null && store.
|
|
1006
|
+
const ask = last?.role === "assistant" && last.turn !== null && store.getLeadTurn(last.turn)?.approver === who.name ? store.leadAskOpen(last.turn, now) : null;
|
|
1007
1007
|
// Answered questions stay readable above the answer; only their buttons go.
|
|
1008
|
-
const asks = new Map(messages.flatMap(one => { const asked = one.role === "assistant" && one.turn !== null ? store.
|
|
1008
|
+
const asks = new Map(messages.flatMap(one => { const asked = one.role === "assistant" && one.turn !== null ? store.leadAsk(one.turn) : null; return asked === null ? [] : [[asked.turn, asked]]; }));
|
|
1009
1009
|
// The thread a ceiling change replaced (ruling 9): read after the open, display only.
|
|
1010
1010
|
const replaced = store.replacedLeadThread(opened.thread);
|
|
1011
|
-
const previousProposals = replaced === null ? [] : store.
|
|
1011
|
+
const previousProposals = replaced === null ? [] : store.listLeadProposals(replaced.id);
|
|
1012
1012
|
return {
|
|
1013
|
-
previous: replaced === null ? null : { messages: store.
|
|
1013
|
+
previous: replaced === null ? null : { messages: store.listLeadMessages(replaced.id, 40), proposals: previousProposals, decisions: decisionsFor(store, previousProposals) },
|
|
1014
1014
|
messages,
|
|
1015
1015
|
ask,
|
|
1016
1016
|
asks,
|
|
@@ -1018,7 +1018,7 @@ export function createDecisionServer(options) {
|
|
|
1018
1018
|
decisions: decisionsFor(store, [...proposals, ...coordinatorProposals]),
|
|
1019
1019
|
coordinatorProposals,
|
|
1020
1020
|
pending: live !== null && live.thread === opened.thread.id ? live : null,
|
|
1021
|
-
recent: store.
|
|
1021
|
+
recent: store.recentLeadTurns(who.name, 5),
|
|
1022
1022
|
};
|
|
1023
1023
|
}
|
|
1024
1024
|
/** The thread a chat surface speaks in (v77): the task's own thread, the
|
|
@@ -1035,21 +1035,21 @@ export function createDecisionServer(options) {
|
|
|
1035
1035
|
if (who.via !== "cookie" || who.role !== "approver" || (focusTask === null && chatProject === null))
|
|
1036
1036
|
return null;
|
|
1037
1037
|
const enabled = chatEnablement();
|
|
1038
|
-
const principal = enabled.ok ?
|
|
1038
|
+
const principal = enabled.ok ? leadPrincipal(who) : null;
|
|
1039
1039
|
if (!enabled.ok || principal === null)
|
|
1040
1040
|
return null;
|
|
1041
|
-
let session = store.
|
|
1041
|
+
let session = store.activeLeadSession(who.name);
|
|
1042
1042
|
if ((session === null || session.ceilingDigest !== principal.ceilingDigest) && enabled.billing === "subscription" && !requestContext.getStore()?.workspaceRead) {
|
|
1043
|
-
|
|
1044
|
-
session = store.
|
|
1043
|
+
startLeadConversation(who, principal, enabled, 0, false, now);
|
|
1044
|
+
session = store.activeLeadSession(who.name);
|
|
1045
1045
|
}
|
|
1046
1046
|
if (session === null || session.ceilingDigest !== principal.ceilingDigest || session.approverGeneration !== principal.generation)
|
|
1047
1047
|
return null;
|
|
1048
|
-
const rows =
|
|
1048
|
+
const rows = leadConversationRows(who, principal, focusTask, now, chatProject);
|
|
1049
1049
|
return {
|
|
1050
|
-
sessionId: session.id, user: session.approver, version:
|
|
1051
|
-
messages:
|
|
1052
|
-
pendingTurnId: rows.pending?.id ?? null, requestId: randomBytes(16).toString("hex"), maxChars:
|
|
1050
|
+
sessionId: session.id, user: session.approver, version: leadChatVersion({ ...rows, focusTask }),
|
|
1051
|
+
messages: leadBrowserMessages(rows, back, { task: focusTask?.id ?? null, project: chatProject }),
|
|
1052
|
+
pendingTurnId: rows.pending?.id ?? null, requestId: randomBytes(16).toString("hex"), maxChars: LEAD_MESSAGE_MAX_CHARS,
|
|
1053
1053
|
taskId: focusTask?.id ?? null, resultRunId, project: focusTask === null ? chatProject : null,
|
|
1054
1054
|
};
|
|
1055
1055
|
}
|
|
@@ -1058,17 +1058,17 @@ export function createDecisionServer(options) {
|
|
|
1058
1058
|
/** Start the person's lead conversation: the session every later turn
|
|
1059
1059
|
* debits (a membership spends nothing; direct API gets a per-conversation
|
|
1060
1060
|
* ceiling) and its thread. Starting ends any older session. */
|
|
1061
|
-
function
|
|
1061
|
+
function startLeadConversation(who, principal, enabled, ceilingUsd, follow, now) {
|
|
1062
1062
|
const ceilingMicrousd = enabled.billing === "subscription" ? 0 : Math.round(ceilingUsd * 1_000_000);
|
|
1063
1063
|
const termsDigest = createHash("sha256").update(`${ceilingMicrousd}\n${principal.ceilingDigest}`).digest("hex");
|
|
1064
|
-
const sessionId = store.
|
|
1065
|
-
const thread = store.
|
|
1064
|
+
const sessionId = store.mintLeadSession({ approver: who.name, approverGeneration: principal.generation, credentialKey: enabled.credentialKey, ceilingMicrousd, ceilingDigest: principal.ceilingDigest, termsDigest }, now);
|
|
1065
|
+
const thread = store.openLeadThread(who.name, principal.ceilingDigest, now).thread;
|
|
1066
1066
|
if (follow)
|
|
1067
|
-
configureLeadFollow(store, principal, store.
|
|
1068
|
-
|
|
1067
|
+
configureLeadFollow(store, principal, store.getLeadSession(sessionId), thread, true, now);
|
|
1068
|
+
leadSaid.delete(who.session.csrf);
|
|
1069
1069
|
return sessionId;
|
|
1070
1070
|
}
|
|
1071
|
-
function
|
|
1071
|
+
function leadPrincipal(who) {
|
|
1072
1072
|
const verified = verifyApproverStanding(store, who.name, who.session.generation, managedRepos());
|
|
1073
1073
|
return verified.ok ? verified.who : null;
|
|
1074
1074
|
}
|
|
@@ -1237,12 +1237,7 @@ export function createDecisionServer(options) {
|
|
|
1237
1237
|
if (s.workspace?.view?.kind === "flow")
|
|
1238
1238
|
s = { ...s, workspace: { ...s.workspace, view: { ...s.workspace.view, stepUp: { label: ssoFacts.label, fresh: ssoFacts.fresh, confirmHref: `/login/sso?reauth=1&return=${encodeURIComponent(back)}` } } } };
|
|
1239
1239
|
}
|
|
1240
|
-
|
|
1241
|
-
if (teamEntry?.startsWith('/chat') && !s.workspace?.team && !teamEntry.includes('task=') && !teamEntry.includes('proposal=') && demoLeadHere() === null) {
|
|
1242
|
-
s.body = html `<p class="team-entry"><a href="/chat?team=1">Open team chat</a></p>${s.body}`;
|
|
1243
|
-
if (s.workspace?.conversation)
|
|
1244
|
-
s.workspace.controlsHtml = html `<p><a href="/chat?team=1">Open team chat</a></p>${s.workspace.controlsHtml}`;
|
|
1245
|
-
}
|
|
1240
|
+
// D5: team chat (the central team service) is deprecated; /chat?team=1 still opens this release, but nothing links to it.
|
|
1246
1241
|
const sensitive = s.forceSensitive === true ||
|
|
1247
1242
|
SENSITIVE_INPUT.test(htmlString(s.body)) ||
|
|
1248
1243
|
(s.chrome?.listPane !== undefined && SENSITIVE_INPUT.test(htmlString(s.chrome.listPane)));
|
|
@@ -1296,7 +1291,7 @@ export function createDecisionServer(options) {
|
|
|
1296
1291
|
try {
|
|
1297
1292
|
const actor = requestFacts.actor ?? '';
|
|
1298
1293
|
if (actor !== '') {
|
|
1299
|
-
chats = store.
|
|
1294
|
+
chats = store.listLeadThreads(actor, 16).flatMap((thread) => {
|
|
1300
1295
|
if (thread.scope.kind === 'project') {
|
|
1301
1296
|
const repo = thread.scope.key;
|
|
1302
1297
|
return visible(repo) && managedRepos().includes(repo) ? [{ kind: 'project', title: projectName(repo), href: projectChatHref(repo), at: thread.lastMessageAt }] : [];
|
|
@@ -1319,7 +1314,7 @@ export function createDecisionServer(options) {
|
|
|
1319
1314
|
...(requestFacts.actor ? { leadName: leadNameOf(store, requestFacts.actor) } : {}),
|
|
1320
1315
|
refreshUrl: path.pathname + path.search,
|
|
1321
1316
|
receipt: conversation !== null && request && REQUEST_TOKEN.test(request)
|
|
1322
|
-
? { request, received: store.
|
|
1317
|
+
? { request, received: store.leadRequestReceipt(conversation.sessionId, request) !== null } : null,
|
|
1323
1318
|
projects: browserProjectsOf(s.chrome.projects ?? []), ...crew,
|
|
1324
1319
|
conversation, ...(extras.team ? { team: extras.team } : {}), focus: extras.focus ?? null, result: extras.result ?? null,
|
|
1325
1320
|
catchUpHtml: extras.catchUpHtml === undefined ? '' : htmlString(extras.catchUpHtml), controlsHtml: extras.controlsHtml === undefined ? '' : htmlString(extras.controlsHtml), notices, view: extras.view ?? null,
|
|
@@ -1564,7 +1559,7 @@ export function createDecisionServer(options) {
|
|
|
1564
1559
|
});
|
|
1565
1560
|
});
|
|
1566
1561
|
// Keep native process custody until shutdown has verified agent/tool exit.
|
|
1567
|
-
// Coding custody does not depend on
|
|
1562
|
+
// Coding custody does not depend on subagents or the lead's follow pass, so
|
|
1568
1563
|
// it closes at once and never waits behind a model turn: a stop must release
|
|
1569
1564
|
// the owner record well inside the service's exit window (Oct 2: deploys
|
|
1570
1565
|
// over 0.9.11 found it still held by a killed process).
|
|
@@ -1645,18 +1640,18 @@ export function createDecisionServer(options) {
|
|
|
1645
1640
|
get flowRooms() { return flowRooms; },
|
|
1646
1641
|
get providerHome() { return providerHome; },
|
|
1647
1642
|
get toolHome() { return toolHome; },
|
|
1648
|
-
get
|
|
1643
|
+
get leadPrincipal() { return leadPrincipal; },
|
|
1649
1644
|
get chatScopeOf() { return chatScopeOf; },
|
|
1650
1645
|
get liveTurns() { return liveTurns; },
|
|
1651
1646
|
get liveTurnStarted() { return liveTurnStarted; },
|
|
1652
|
-
get
|
|
1647
|
+
get leadConversationRows() { return leadConversationRows; },
|
|
1653
1648
|
get demoLeadHere() { return demoLeadHere; },
|
|
1654
1649
|
get teamBrowserReply() { return teamBrowserReply; },
|
|
1655
1650
|
get team() { return team; },
|
|
1656
1651
|
get teamChatProvider() { return teamChatProvider; },
|
|
1657
1652
|
get pullRequestTargetOf() { return pullRequestTargetOf; },
|
|
1658
1653
|
get chatEnablement() { return chatEnablement; },
|
|
1659
|
-
get
|
|
1654
|
+
get startLeadConversation() { return startLeadConversation; },
|
|
1660
1655
|
get projectFamilyPeek() { return projectFamilyPeek; },
|
|
1661
1656
|
get needsYouBadge() { return needsYouBadge; },
|
|
1662
1657
|
get liveRefreshSeconds() { return liveRefreshSeconds; },
|
|
@@ -1689,7 +1684,7 @@ export function createDecisionServer(options) {
|
|
|
1689
1684
|
get deletedRepos() { return deletedRepos; },
|
|
1690
1685
|
get lookupSession() { return lookupSession; },
|
|
1691
1686
|
get authorizeMutation() { return authorizeMutation; },
|
|
1692
|
-
get
|
|
1687
|
+
get leadSaid() { return leadSaid; },
|
|
1693
1688
|
get armTaskResume() { return armTaskResume; },
|
|
1694
1689
|
get chatFetcher() { return chatFetcher; },
|
|
1695
1690
|
get chatCeilingDigest() { return chatCeilingDigest; },
|
package/dist/server/chat.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type BrowserFirstRun, type BrowserHome, type BrowserPhoneCard } from '.
|
|
|
2
2
|
import { type DemoResultView } from "../demo-chat.js";
|
|
3
3
|
import type { DemoExchange } from "../demo.js";
|
|
4
4
|
import { type FirstTaskSuggestion } from "../first-run.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { LeadProgress } from "../lead-progress.js";
|
|
6
6
|
import type { DirectChatProviderId } from "../store.js";
|
|
7
7
|
import type { ServerRuntime } from './runtime.js';
|
|
8
8
|
import { type ChatEnablement } from "./render-chat.js";
|
|
@@ -14,10 +14,10 @@ export declare function createChatHandlers(runtime: ServerRuntime): {
|
|
|
14
14
|
via: "cookie";
|
|
15
15
|
}, now: Date) => BrowserPhoneCard | undefined;
|
|
16
16
|
chatProjectOf: (values: string[]) => string | null | undefined;
|
|
17
|
-
beginLiveTurn: (thread: number) => (event:
|
|
17
|
+
beginLiveTurn: (thread: number) => (event: LeadProgress) => void;
|
|
18
18
|
endLiveTurn: (thread: number, ok: boolean) => void;
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
noteLead: (csrf: string, turn: number | null, message: string) => void;
|
|
20
|
+
takeLeadNote: (csrf: string, liveSession: number | null) => string | null;
|
|
21
21
|
demoResultView: (exchange: DemoExchange) => DemoResultView | null;
|
|
22
22
|
storeChatKey: (provider: DirectChatProviderId, key: string) => {
|
|
23
23
|
ok: true;
|