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/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;
|
package/dist/server/chat.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { handlersOf } from './handler-registry.js';
|
|
2
|
+
import { DEPRECATED_PAGE } from "../deprecations.js";
|
|
2
3
|
/** chat handlers, moved without changing their route bodies. */
|
|
3
4
|
import { randomBytes } from "node:crypto";
|
|
4
5
|
import { chmodSync, rmSync as rmFileSync, writeFileSync as writeFsFileSync } from "node:fs";
|
|
@@ -18,8 +19,8 @@ import { leadBriefHtml } from '../lead-context.js';
|
|
|
18
19
|
import { configureLeadFollow, leadFollowStatus } from '../lead-follow.js';
|
|
19
20
|
import { leadActivity } from "../lead-voice.js";
|
|
20
21
|
import { limitsView } from "../limits-ui.js";
|
|
21
|
-
import { confirmCoordinatorProposal,
|
|
22
|
-
import {
|
|
22
|
+
import { confirmCoordinatorProposal, confirmLeadProposal, dismissCoordinatorProposal, dismissLeadProposal } from "../lead-doors.js";
|
|
23
|
+
import { LEAD_MESSAGE_MAX_CHARS, runLeadTurn } from "../lead.js";
|
|
23
24
|
import { livePin, modelOptions } from "../model-catalog.js";
|
|
24
25
|
import { verifyApproverByPassword, verifyApproverStanding } from "../principal.js";
|
|
25
26
|
import { projectName } from "../project.js";
|
|
@@ -37,12 +38,12 @@ import { html, htmlString, postForm } from "../html.js";
|
|
|
37
38
|
import { requestContext } from "./request-context.js";
|
|
38
39
|
import { refuse, screen } from "./chrome.js";
|
|
39
40
|
import { chatResultHref, chatReturnWithLatest, chatReturnWithSaid, projectChatHref, redirect, respond, safeChatReturn, safeReturn, taskChatHref, taskHref } from "./http.js";
|
|
40
|
-
import { chatAckPage, chatPage, coordinatorProposalsSection, decisionsFor,
|
|
41
|
+
import { chatAckPage, chatPage, coordinatorProposalsSection, decisionsFor, leadAfterComposerHtml, leadChatVersion, leadMintCard, leadPage, leadThreadHtml, PHONE_CARD_FACT, TASK_COMPOSER_MODES, taskChatLiveRegion, teamProposalCardParts } from "./render-chat.js";
|
|
41
42
|
import {} from "./render-pages.js";
|
|
42
|
-
import { completionForm, homePhaseWords, owedAcceptanceOf, resultPanelHtml } from "./render-results.js";
|
|
43
|
+
import { completionForm, hardBlockerOf, homePhaseWords, owedAcceptanceOf, resultPanelHtml } from "./render-results.js";
|
|
43
44
|
import { browserCaller, oneLineUa } from "./session.js";
|
|
44
45
|
export function createChatHandlers(runtime) {
|
|
45
|
-
const { firstRunStepsNow, managedRepos, leadWords, store, options, phoneSetup, visible, liveTurns,
|
|
46
|
+
const { firstRunStepsNow, managedRepos, leadWords, store, options, phoneSetup, visible, liveTurns, leadSaid, evidenceRoot, clock, sessions, CHAT_CANDIDATE_TTL_MS, chatFetcher, CHAT_CANDIDATES_PER_APPROVER, chatCeilingDigest, workAccess, familyOf, firstTasks, leadPrincipal, taskChatFocus, chatScopeOf, leadConversationRows, demoLeadHere, sendScreen, chromeFor, teamBrowserReply, team, teamChatProvider, runVisible, runIsLive, resultDetailOf, pullRequestTargetOf, chatEnablement, startLeadConversation, projectFamilyPeek, needsYouBadge, liveRefreshSeconds, chatKeyFor, chatCatalog, providerHome, mintApprovalNonce, checkLocalAgents, agentSignInCommand, bustBadge, authenticateApprover, revisionDestination, armTaskResume, consumeApprovalNonce } = runtime;
|
|
46
47
|
// The read-only refresh (package 2): the same JSON status as before —
|
|
47
48
|
// session binding, the send receipt, the live turn — plus a version
|
|
48
49
|
// of the DISPLAYED facts and, only when the caller's version differs,
|
|
@@ -52,14 +53,14 @@ export function createChatHandlers(runtime) {
|
|
|
52
53
|
async function mateStatus(ctx) {
|
|
53
54
|
const { url, response, now } = ctx;
|
|
54
55
|
const who = browserCaller(ctx.who);
|
|
55
|
-
const session = store.
|
|
56
|
-
const principal =
|
|
56
|
+
const session = store.activeLeadSession(who.name);
|
|
57
|
+
const principal = leadPrincipal(who);
|
|
57
58
|
if (session === null || principal === null || session.ceilingDigest !== principal.ceilingDigest || session.approverGeneration !== principal.generation) {
|
|
58
59
|
return respond(response, 200, "application/json", JSON.stringify({ session: null }));
|
|
59
60
|
}
|
|
60
|
-
store.
|
|
61
|
+
store.sweepStaleLeadTurns(now);
|
|
61
62
|
const request = url.searchParams.get("request") ?? "";
|
|
62
|
-
const receipt = /^[a-f0-9]{32}$/.test(request) ? store.
|
|
63
|
+
const receipt = /^[a-f0-9]{32}$/.test(request) ? store.leadRequestReceipt(session.id, request) : null;
|
|
63
64
|
const requestedTask = url.searchParams.get("task");
|
|
64
65
|
const focusTask = requestedTask === null || requestedTask === "" ? null : taskChatFocus(requestedTask, now, who, { mintNonce: false });
|
|
65
66
|
if (requestedTask !== null && requestedTask !== "" && focusTask === null) {
|
|
@@ -70,15 +71,15 @@ export function createChatHandlers(runtime) {
|
|
|
70
71
|
const chatProject = focusTask !== null ? null : chatProjectOf(url.searchParams.getAll("project").filter(one => one !== ""));
|
|
71
72
|
if (chatProject === undefined)
|
|
72
73
|
return respond(response, 200, "application/json", JSON.stringify({ session: session.id, unavailable: true, received: receipt !== null }));
|
|
73
|
-
const rows =
|
|
74
|
-
const version =
|
|
74
|
+
const rows = leadConversationRows(who, principal, focusTask, now, chatProject);
|
|
75
|
+
const version = leadChatVersion({ ...rows, focusTask });
|
|
75
76
|
const known = url.searchParams.get("version") ?? "";
|
|
76
77
|
const csrf = who.session.csrf;
|
|
77
78
|
const fragments = known === version
|
|
78
79
|
? null
|
|
79
80
|
: {
|
|
80
|
-
thread: htmlString(
|
|
81
|
-
after: htmlString(
|
|
81
|
+
thread: htmlString(leadThreadHtml({ ...rows, focusTask, csrf, now, problem: takeLeadNote(csrf, session.id), chatProject })),
|
|
82
|
+
after: htmlString(leadAfterComposerHtml({ messages: rows.messages, pending: rows.pending, focusTask })),
|
|
82
83
|
live: focusTask === null ? null : htmlString(taskChatLiveRegion(focusTask, csrf, true, rows.pending !== null)),
|
|
83
84
|
};
|
|
84
85
|
return respond(response, 200, "application/json", JSON.stringify({
|
|
@@ -118,7 +119,8 @@ export function createChatHandlers(runtime) {
|
|
|
118
119
|
// THIS session's memory; a bearer caller has nowhere to keep them.
|
|
119
120
|
if (who.via !== "cookie")
|
|
120
121
|
return refuse(response, who, 403, "chat is a browser surface — it keeps your drafts in the session");
|
|
121
|
-
|
|
122
|
+
const teamAsked = url.searchParams.get('team') === '1' || url.searchParams.has('conversation') || url.searchParams.has('lead') || url.searchParams.has('proposal');
|
|
123
|
+
if (teamAsked && url.searchParams.get('private') !== '1' && !url.searchParams.has('task') && !url.searchParams.has('result') && !url.searchParams.has('project')) {
|
|
122
124
|
const reply = teamBrowserReply(await team.execute({ name: who.name, generation: who.session.generation }, {
|
|
123
125
|
operation: url.searchParams.has('conversation') ? 'show' : 'list',
|
|
124
126
|
args: { ...(url.searchParams.has('conversation') ? { conversationId: url.searchParams.get('conversation') } : {}),
|
|
@@ -128,7 +130,7 @@ export function createChatHandlers(runtime) {
|
|
|
128
130
|
return refuse(response, who, 403, reply.message, '/chat?private=1');
|
|
129
131
|
if (url.searchParams.has('proposal')) {
|
|
130
132
|
const id = Number(url.searchParams.get('proposal')), selected = reply.snapshot.selected;
|
|
131
|
-
const proposal = Number.isSafeInteger(id) && id > 0 ? store.
|
|
133
|
+
const proposal = Number.isSafeInteger(id) && id > 0 ? store.getLeadProposal(id) : null;
|
|
132
134
|
if (!selected || !proposal || proposal.thread !== selected.threadId)
|
|
133
135
|
return refuse(response, who, 404, 'This proposal is unavailable.', '/chat');
|
|
134
136
|
const back = '/chat?conversation=' + encodeURIComponent(selected.id);
|
|
@@ -136,15 +138,13 @@ export function createChatHandlers(runtime) {
|
|
|
136
138
|
const card = teamProposalCardParts(store, { name: who.name, generation: who.session.generation }, proposal, reply.snapshot, who.session.csrf, decision, clock(), teamChatProvider).html;
|
|
137
139
|
return sendScreen(response, 200, screen('Review action', html `<p><a href="${back}">Back to conversation</a></p>${card}`, { chrome: chromeFor(null, 'chat', undefined, 'all') }));
|
|
138
140
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}));
|
|
144
|
-
}
|
|
141
|
+
const said = url.searchParams.get('said')?.slice(0, 1_000);
|
|
142
|
+
return sendScreen(response, 200, screen('Chat', html `<p class="problem" role="status" data-deprecated="team">${DEPRECATED_PAGE.team}</p>${said ? html `<p role="alert">${said}</p>` : ''}${teamWorkspaceHtml(reply.snapshot)}`, {
|
|
143
|
+
chrome: chromeFor(null, 'chat', undefined, 'all'), workspace: { team: reply.snapshot, notices: [DEPRECATED_PAGE.team, ...(said ? [said] : [])] },
|
|
144
|
+
}));
|
|
145
145
|
}
|
|
146
146
|
store.sweepStaleChatTurns(now);
|
|
147
|
-
store.
|
|
147
|
+
store.sweepStaleLeadTurns(now);
|
|
148
148
|
store.sweepCoordinatorProposals(now);
|
|
149
149
|
sweepChatDrafts(Date.now());
|
|
150
150
|
const requestedTask = url.searchParams.get("task");
|
|
@@ -185,7 +185,9 @@ export function createChatHandlers(runtime) {
|
|
|
185
185
|
const roomId = url.searchParams.get('conversation');
|
|
186
186
|
const resultLink = (href) => roomId ? href + '&conversation=' + encodeURIComponent(roomId) : href;
|
|
187
187
|
// Accept and finish is here: the one act that accepts the person's own checks and finishes the task.
|
|
188
|
-
|
|
188
|
+
// Never offered where Complete is refused whatever is accepted (a failed check, a HIGH finding).
|
|
189
|
+
const finishes = resultRun !== null && who.role === 'approver' && focusTask?.assignment?.state === 'ready-to-check' && focusTask.assignment.receipt?.runId === resultRun.id &&
|
|
190
|
+
hardBlockerOf(focusTask.assignment) === null;
|
|
189
191
|
const resultPanel = resultRun === null
|
|
190
192
|
? null
|
|
191
193
|
: html `${resultPanelHtml(resultDetailOf(resultRun, who, now), {
|
|
@@ -233,8 +235,8 @@ export function createChatHandlers(runtime) {
|
|
|
233
235
|
// The mate (mate arc §5): while a mate session is live, /chat IS the
|
|
234
236
|
// thread — the same rows the CLI reads. Without one, fleet chat as
|
|
235
237
|
// before, plus the card that mints a session.
|
|
236
|
-
let
|
|
237
|
-
const principal = enabled.ok && who.role === "approver" ?
|
|
238
|
+
let leadSession = enabled.ok && who.role === "approver" ? store.activeLeadSession(who.name) : null;
|
|
239
|
+
const principal = enabled.ok && who.role === "approver" ? leadPrincipal(who) : null;
|
|
238
240
|
// Chat opens ready to talk (2026-09-23): an approver in good standing
|
|
239
241
|
// needs no second password to start their own conversation. The one
|
|
240
242
|
// exception to "a GET writes nothing": the strict same-site session
|
|
@@ -243,14 +245,14 @@ export function createChatHandlers(runtime) {
|
|
|
243
245
|
// Membership chat only: it spends no dollars. Direct-API chat keeps a
|
|
244
246
|
// one-tap Start with its visible spending limit (no password).
|
|
245
247
|
const settingsView = url.searchParams.get("settings") === "1";
|
|
246
|
-
if (!settingsView && enabled.ok && enabled.billing === "subscription" && principal !== null && (
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
if (!settingsView && enabled.ok && enabled.billing === "subscription" && principal !== null && (leadSession === null || leadSession.ceilingDigest !== principal.ceilingDigest) && !requestContext.getStore()?.workspaceRead) {
|
|
249
|
+
startLeadConversation(who, principal, enabled, 0, false, now);
|
|
250
|
+
leadSession = store.activeLeadSession(who.name);
|
|
249
251
|
}
|
|
250
252
|
// A session under another ceiling is not continuable from here; a GET
|
|
251
253
|
// writes nothing (slice-2 review, finding 7) — the mint card below
|
|
252
254
|
// starts a new conversation, and minting ends the old session.
|
|
253
|
-
const ceilingStale = enabled.ok &&
|
|
255
|
+
const ceilingStale = enabled.ok && leadSession !== null && principal !== null && leadSession.ceilingDigest !== principal.ceilingDigest;
|
|
254
256
|
const catchUp = leadBriefHtml(assignmentCatchUp(store, now, { principal: "operator", repos }, { limit: 6 }, evidenceRoot));
|
|
255
257
|
const chatProjects = repos.map((repo, index) => {
|
|
256
258
|
let peek = null;
|
|
@@ -267,7 +269,7 @@ export function createChatHandlers(runtime) {
|
|
|
267
269
|
if (repos.length > 0) {
|
|
268
270
|
try {
|
|
269
271
|
fleetSnapshot = store.chatSnapshot(repos, now);
|
|
270
|
-
const summaries = workIndexPage(store, now, { principal: 'operator', repos, includeUnplaced: false, viewer: who.name }, { limit: 100 }).items;
|
|
272
|
+
const summaries = workIndexPage(store, now, { principal: 'operator', repos, includeUnplaced: false, viewer: who.name }, { limit: 100, root: evidenceRoot }).items;
|
|
271
273
|
fleetSnapshot.assignmentStates = Object.fromEntries(summaries.flatMap(value => [value.rootId, value.activeTaskId].map(id => [id, { state: value.assignmentState, label: value.status.label, detail: value.status.detail }])));
|
|
272
274
|
fleetSnapshot.attentionCount = needsYouBadge(null);
|
|
273
275
|
}
|
|
@@ -284,12 +286,12 @@ export function createChatHandlers(runtime) {
|
|
|
284
286
|
const withFirstRun = (shown) => firstRun === undefined && phone === undefined && home === null ? shown
|
|
285
287
|
: { ...shown, ...(home === null ? {} : { refreshSeconds: liveRefreshSeconds() }),
|
|
286
288
|
workspace: { ...shown.workspace, ...(firstRun === undefined ? {} : { firstRun }), ...(phone === undefined ? {} : { phone }), ...(home === null ? {} : { home }) } };
|
|
287
|
-
if (enabled.ok &&
|
|
289
|
+
if (enabled.ok && leadSession !== null && principal !== null && !ceilingStale) {
|
|
288
290
|
{
|
|
289
|
-
const said =
|
|
290
|
-
return sendScreen(response, 200, withFirstRun(
|
|
291
|
-
session:
|
|
292
|
-
...
|
|
291
|
+
const said = takeLeadNote(who.session.csrf, leadSession.id);
|
|
292
|
+
return sendScreen(response, 200, withFirstRun(leadPage(chromeFor(null, "chat", undefined, "all"), {
|
|
293
|
+
session: leadSession,
|
|
294
|
+
...leadConversationRows(who, principal, focusTask, now, chatProject),
|
|
293
295
|
chatProject,
|
|
294
296
|
latched,
|
|
295
297
|
config: enabled.config,
|
|
@@ -338,9 +340,9 @@ export function createChatHandlers(runtime) {
|
|
|
338
340
|
problem: url.searchParams.get("said") ??
|
|
339
341
|
focusProblem ??
|
|
340
342
|
(ceilingStale ? "Project access changed. Your tasks and results are saved. Start a conversation with the current projects to continue." : null) ??
|
|
341
|
-
|
|
343
|
+
takeLeadNote(who.session.csrf, null),
|
|
342
344
|
resultPanel,
|
|
343
|
-
...(enabled.ok && who.role === "approver" ? {
|
|
345
|
+
...(enabled.ok && who.role === "approver" ? { leadMint: leadMintCard(enabled, focusTask === null ? "/chat" : taskChatHref(focusTask.id)) } : {}),
|
|
344
346
|
...(who.role === "approver" ? { coordinatorProposals: coordinatorProposalsSection(coordinatorRows, decisionsFor(store, coordinatorRows), now, true, focusTask === null ? null : taskChatHref(focusTask.id)) } : {}),
|
|
345
347
|
})));
|
|
346
348
|
}
|
|
@@ -361,7 +363,7 @@ export function createChatHandlers(runtime) {
|
|
|
361
363
|
const { url, response } = ctx;
|
|
362
364
|
const who = browserCaller(ctx.who);
|
|
363
365
|
const proposalId = Number(url.pathname.split("/")[3]);
|
|
364
|
-
const reviewProposal = store.
|
|
366
|
+
const reviewProposal = store.getLeadProposal(proposalId);
|
|
365
367
|
const sharedConversation = reviewProposal ? store.handle.prepare('SELECT id FROM team_conversation WHERE thread=?').get(reviewProposal.thread) : null;
|
|
366
368
|
let principal = null;
|
|
367
369
|
if (sharedConversation) {
|
|
@@ -375,12 +377,12 @@ export function createChatHandlers(runtime) {
|
|
|
375
377
|
}
|
|
376
378
|
}
|
|
377
379
|
else
|
|
378
|
-
principal =
|
|
380
|
+
principal = leadPrincipal(who);
|
|
379
381
|
if (principal === null)
|
|
380
382
|
return refuse(response, who, 403, 'Your access changed. Sign in again.', '/chat');
|
|
381
383
|
try {
|
|
382
|
-
const id = proposalId, saved = store.
|
|
383
|
-
if (saved && savedAction && saved.state !== 'pending' && (sharedConversation !== null || store.
|
|
384
|
+
const id = proposalId, saved = store.getLeadProposal(id), savedAction = saved?.kind === 'action' ? sharedActionPayload(saved.payload) : null;
|
|
385
|
+
if (saved && savedAction && saved.state !== 'pending' && (sharedConversation !== null || store.getLeadThread(saved.thread)?.approver === principal.name) && principal.repos.includes(savedAction.repo) && store.accountCanAccess(principal.name, savedAction.repo)) {
|
|
384
386
|
const task = typeof saved.outcome?.['taskId'] === 'string' ? saved.outcome['taskId'] : typeof savedAction.request['task'] === 'string' ? savedAction.request['task'] : null;
|
|
385
387
|
const destination = task ? taskHref(task) : `/settings/${savedAction.operation.startsWith('skill_') ? 'skills' : 'knowledge'}?repo=${encodeURIComponent(savedAction.repo)}`;
|
|
386
388
|
const said = typeof saved.outcome?.['said'] === 'string' ? saved.outcome['said'] : saved.state === 'dismissed' ? 'Action dismissed.' : saved.state === 'expired' ? 'This proposal expired. Ask for a fresh proposal.' : 'This action has no completed outcome yet.';
|
|
@@ -459,7 +461,7 @@ export function createChatHandlers(runtime) {
|
|
|
459
461
|
: verb === "change" ? lead.change(id, note, now)
|
|
460
462
|
: verb === "revise" ? lead.requestChanges(id, note, now)
|
|
461
463
|
: lead.complete(id, now, (taskId, runId) => {
|
|
462
|
-
const principal =
|
|
464
|
+
const principal = leadPrincipal(who);
|
|
463
465
|
if (principal === null)
|
|
464
466
|
return { ok: false, message: "Your access changed. Sign in again." };
|
|
465
467
|
const current = assignmentOf(store, taskId, now, { principal: "operator", repos: principal.repos }, evidenceRoot);
|
|
@@ -613,11 +615,11 @@ export function createChatHandlers(runtime) {
|
|
|
613
615
|
return redirect(response, back);
|
|
614
616
|
}
|
|
615
617
|
// ---- the mate (mate arc §5) ------------------------------------------
|
|
616
|
-
async function
|
|
618
|
+
async function leadMint(ctx) {
|
|
617
619
|
const { who, response, now, posted } = ctx;
|
|
618
|
-
const body = readForm(posted, CONSOLE_FORMS.
|
|
620
|
+
const body = readForm(posted, CONSOLE_FORMS.leadMint);
|
|
619
621
|
if (who.via !== "cookie")
|
|
620
|
-
return refuse(response, who, 403, "the
|
|
622
|
+
return refuse(response, who, 403, "the lead is a browser surface");
|
|
621
623
|
const back = safeChatReturn(body.get("return"));
|
|
622
624
|
const enabled = chatEnablement();
|
|
623
625
|
if (!enabled.ok)
|
|
@@ -636,60 +638,60 @@ export function createChatHandlers(runtime) {
|
|
|
636
638
|
const verified = token === "" ? verifyApproverStanding(store, who.name, who.session.generation, managedRepos()) : verifyApproverByPassword(store, who.name, token, managedRepos());
|
|
637
639
|
if (!verified.ok)
|
|
638
640
|
return redirect(response, chatReturnWithSaid(back, token === "" ? "Your access changed. Sign in again to start chat." : "That password did not match."));
|
|
639
|
-
|
|
641
|
+
startLeadConversation(who, verified.who, enabled, ceilingUsd, body.get("follow") === "yes", now);
|
|
640
642
|
return redirect(response, back);
|
|
641
643
|
}
|
|
642
|
-
async function
|
|
644
|
+
async function leadFollow(ctx) {
|
|
643
645
|
const { who, response, now, posted } = ctx;
|
|
644
|
-
const body = readForm(posted, CONSOLE_FORMS.
|
|
646
|
+
const body = readForm(posted, CONSOLE_FORMS.leadFollow);
|
|
645
647
|
if (who.via !== "cookie")
|
|
646
648
|
return refuse(response, who, 403, "Open the conversation to change automatic updates.");
|
|
647
|
-
const principal =
|
|
649
|
+
const principal = leadPrincipal(who), session = store.activeLeadSession(who.name), thread = store.liveLeadThreadFor(who.name);
|
|
648
650
|
if (!principal || !session || !thread || !configureLeadFollow(store, principal, session, thread, body.get("enabled") === "yes", now))
|
|
649
651
|
return refuse(response, who, 409, "Conversation access changed. Start chat again.", "/chat");
|
|
650
652
|
return redirect(response, safeChatReturn(body.get("return")));
|
|
651
653
|
}
|
|
652
|
-
async function
|
|
654
|
+
async function leadEnd(ctx) {
|
|
653
655
|
const { who, response, now, posted } = ctx;
|
|
654
|
-
const body = readForm(posted, CONSOLE_FORMS.
|
|
656
|
+
const body = readForm(posted, CONSOLE_FORMS.leadEnd);
|
|
655
657
|
if (who.via !== "cookie")
|
|
656
|
-
return refuse(response, who, 403, "the
|
|
658
|
+
return refuse(response, who, 403, "the lead is a browser surface");
|
|
657
659
|
const back = safeChatReturn(body.get("return"));
|
|
658
660
|
// Ending spend and forgetting the thread takes no password: any
|
|
659
661
|
// approver may revoke (§1), and the thread is theirs to drop (ruling 11).
|
|
660
|
-
store.
|
|
661
|
-
store.
|
|
662
|
-
store.
|
|
663
|
-
|
|
662
|
+
store.failLiveLeadTurnsFor(who.name, "ended", now);
|
|
663
|
+
store.endLeadSessionsFor(who.name, who.name, now);
|
|
664
|
+
store.closeLeadThreadsFor(who.name, now);
|
|
665
|
+
leadSaid.delete(who.session.csrf);
|
|
664
666
|
return redirect(response, back);
|
|
665
667
|
}
|
|
666
|
-
async function
|
|
668
|
+
async function leadStop(ctx) {
|
|
667
669
|
const { who, response, now, posted } = ctx;
|
|
668
|
-
const body = readForm(posted, CONSOLE_FORMS.
|
|
670
|
+
const body = readForm(posted, CONSOLE_FORMS.leadStop);
|
|
669
671
|
if (who.via !== "cookie")
|
|
670
|
-
return refuse(response, who, 403, "the
|
|
672
|
+
return refuse(response, who, 403, "the lead is a browser surface");
|
|
671
673
|
const back = safeChatReturn(body.get("return"));
|
|
672
674
|
const wanted = Number(body.get("turn") ?? "");
|
|
673
|
-
const live = store.
|
|
675
|
+
const live = store.liveLeadTurnFor(who.name);
|
|
674
676
|
if (!Number.isInteger(wanted) || live === null || live.id !== wanted) {
|
|
675
|
-
|
|
677
|
+
noteLead(who.session.csrf, null, "that turn has already finished");
|
|
676
678
|
return redirect(response, chatReturnWithLatest(back));
|
|
677
679
|
}
|
|
678
680
|
// A stopped direct-API turn is conservatively charged its reserved
|
|
679
681
|
// worst case: dispatch may already have happened. Membership turns
|
|
680
682
|
// reserve zero. The conversation itself stays live.
|
|
681
|
-
store.
|
|
682
|
-
|
|
683
|
+
store.failLiveLeadTurnsFor(who.name, "stopped", now);
|
|
684
|
+
noteLead(who.session.csrf, live.id, "stopped — the conversation is still open");
|
|
683
685
|
return redirect(response, chatReturnWithLatest(back));
|
|
684
686
|
}
|
|
685
687
|
const chatProposalAct = (op) => async (ctx) => {
|
|
686
688
|
const { url, who, request, response, now, posted } = ctx;
|
|
687
|
-
const body = readForm(posted, CONSOLE_FORMS.
|
|
689
|
+
const body = readForm(posted, CONSOLE_FORMS.leadProposal);
|
|
688
690
|
if (who.via !== "cookie")
|
|
689
|
-
return refuse(response, who, 403, "the
|
|
691
|
+
return refuse(response, who, 403, "the lead is a browser surface");
|
|
690
692
|
const back = safeChatReturn(body.get("return"));
|
|
691
693
|
const id = Number(url.pathname.split("/")[3]);
|
|
692
|
-
const proposalRow = store.
|
|
694
|
+
const proposalRow = store.getLeadProposal(id);
|
|
693
695
|
const shared = proposalRow && store.handle.prepare('SELECT id FROM team_conversation WHERE thread=?').get(proposalRow.thread);
|
|
694
696
|
const proposalTaskHref = (taskId) => taskChatHref(taskId) + (shared ? '&conversation=' + encodeURIComponent(String(shared['id'])) : '');
|
|
695
697
|
let principal = null;
|
|
@@ -704,7 +706,7 @@ export function createChatHandlers(runtime) {
|
|
|
704
706
|
}
|
|
705
707
|
}
|
|
706
708
|
else
|
|
707
|
-
principal =
|
|
709
|
+
principal = leadPrincipal(who);
|
|
708
710
|
// Confirm in place (chat cards): the same door and checks, answered in
|
|
709
711
|
// JSON so the conversation stays where it is; the card's refreshed
|
|
710
712
|
// state carries the result. The secure review screen stays a form.
|
|
@@ -713,14 +715,14 @@ export function createChatHandlers(runtime) {
|
|
|
713
715
|
if (principal === null)
|
|
714
716
|
return cardJson ? cardAnswer(403, false, "Your approver standing changed. Sign in again.") : refuse(response, who, 403, "your approver standing changed — sign in again", back);
|
|
715
717
|
if (op === "dismiss") {
|
|
716
|
-
const dismissed =
|
|
718
|
+
const dismissed = dismissLeadProposal(store, principal, id, now);
|
|
717
719
|
if (cardJson)
|
|
718
720
|
return cardAnswer(dismissed ? 200 : 409, dismissed, dismissed ? "Dismissed." : "That card was already acted on.");
|
|
719
721
|
if (!dismissed)
|
|
720
|
-
|
|
722
|
+
noteLead(who.session.csrf, null, "that proposal was already acted on");
|
|
721
723
|
return redirect(response, chatReturnWithLatest(back));
|
|
722
724
|
}
|
|
723
|
-
const outcome =
|
|
725
|
+
const outcome = confirmLeadProposal(store, principal, id, now, { chatProvider: teamChatProvider, confirm: body.get("confirm") === "yes", via: "web", evidenceRoot, ...(body.has("nonce") ? { actionReview: { nonce: body.get("nonce") ?? "", password: body.get("token") ?? "" } } : {}) });
|
|
724
726
|
if (cardJson) {
|
|
725
727
|
if (!outcome.ok)
|
|
726
728
|
return cardAnswer(outcome.reason === "standing" ? 403 : outcome.reason === "not-yours" ? 404 : 409, false, outcome.said);
|
|
@@ -737,7 +739,7 @@ export function createChatHandlers(runtime) {
|
|
|
737
739
|
if (!outcome.ok && shared)
|
|
738
740
|
return redirect(response, chatReturnWithSaid(back, outcome.said));
|
|
739
741
|
if (!outcome.ok && outcome.reason === "needs-confirm")
|
|
740
|
-
|
|
742
|
+
noteLead(who.session.csrf, null, outcome.said);
|
|
741
743
|
// A confirmed task proposal leads to the task it actually created
|
|
742
744
|
// (package 2): the lens over this same conversation, whose journey
|
|
743
745
|
// and plan are the next step. The id is the door's recorded
|
|
@@ -748,7 +750,7 @@ export function createChatHandlers(runtime) {
|
|
|
748
750
|
return redirect(response, `${proposalTaskHref(outcome.taskId)}#task-chat-live`);
|
|
749
751
|
}
|
|
750
752
|
if (outcome.ok && outcome.kind === "review" && outcome.taskId !== null) {
|
|
751
|
-
const proposal = store.
|
|
753
|
+
const proposal = store.getLeadProposal(id);
|
|
752
754
|
// A revision confirmed here returns to its receipt: the newest
|
|
753
755
|
// reply, whose confirmed card names the revision and links to the
|
|
754
756
|
// plan. Landing at the top left that message under the phone's
|
|
@@ -758,7 +760,7 @@ export function createChatHandlers(runtime) {
|
|
|
758
760
|
: `${proposalTaskHref(outcome.taskId)}&result=${Number(proposal.payload["run"])}#request-changes`);
|
|
759
761
|
}
|
|
760
762
|
if (outcome.ok && outcome.kind === "task_action" && outcome.taskId !== null) {
|
|
761
|
-
const proposal = store.
|
|
763
|
+
const proposal = store.getLeadProposal(id);
|
|
762
764
|
if (proposal.payload["operation"] === "resume") {
|
|
763
765
|
return armTaskResume(response, who, outcome.taskId, String(proposal.payload["run"]), "chat", now);
|
|
764
766
|
}
|
|
@@ -773,7 +775,7 @@ export function createChatHandlers(runtime) {
|
|
|
773
775
|
const body = readForm(posted, CONSOLE_FORMS.coordinatorProposal);
|
|
774
776
|
if (who.via !== "cookie")
|
|
775
777
|
return refuse(response, who, 403, "proposals are confirmed from the browser or the CLI");
|
|
776
|
-
const principal =
|
|
778
|
+
const principal = leadPrincipal(who);
|
|
777
779
|
if (principal === null)
|
|
778
780
|
return refuse(response, who, 403, "your approver standing changed — sign in again", "/chat");
|
|
779
781
|
const id = Number(url.pathname.split("/")[2]);
|
|
@@ -835,34 +837,34 @@ export function createChatHandlers(runtime) {
|
|
|
835
837
|
// A live mate session: the message is a mate turn — no password, the
|
|
836
838
|
// session's ceremony already covered it (§1); the engine refuses on
|
|
837
839
|
// its own terms and the thread shows why.
|
|
838
|
-
const
|
|
839
|
-
if (
|
|
840
|
+
const leadSession = who.role === "approver" ? store.activeLeadSession(who.name) : null;
|
|
841
|
+
if (leadSession !== null) {
|
|
840
842
|
const requestId = body.get("request");
|
|
841
|
-
if (requestId !== null && (body.getAll("request").length !== 1 || body.getAll("request-session").length !== 1 || body.get("request-session") !== String(
|
|
843
|
+
if (requestId !== null && (body.getAll("request").length !== 1 || body.getAll("request-session").length !== 1 || body.get("request-session") !== String(leadSession.id))) {
|
|
842
844
|
return said(409, "This conversation changed. Reload it before sending your message.");
|
|
843
845
|
}
|
|
844
|
-
const principal =
|
|
846
|
+
const principal = leadPrincipal(who);
|
|
845
847
|
if (principal === null)
|
|
846
848
|
return wantsJson ? said(403, "your approver standing changed — sign in again") : refuse(response, who, 403, "your approver standing changed — sign in again", back);
|
|
847
849
|
const message = (body.get("message") ?? "").trim();
|
|
848
|
-
if (message === "" || message.length >
|
|
849
|
-
return said(400, `a message is 1 to ${
|
|
850
|
+
if (message === "" || message.length > LEAD_MESSAGE_MAX_CHARS) {
|
|
851
|
+
return said(400, `a message is 1 to ${LEAD_MESSAGE_MAX_CHARS} characters`, leadSession.id);
|
|
850
852
|
}
|
|
851
|
-
const opened = store.
|
|
853
|
+
const opened = store.openLeadThread(who.name, principal.ceilingDigest, now, chatScopeOf(focusTask, chatProject));
|
|
852
854
|
const onProgress = beginLiveTurn(opened.thread.id);
|
|
853
|
-
void
|
|
855
|
+
void runLeadTurn({ store, who: principal, session: leadSession, thread: opened.thread, config: enabled.config, key: enabled.key, message, onProgress, channel: "console", ...(requestId === null ? {} : { requestId }), ...(focusTask === null && chatProject !== null ? { context: `Current project: ${projectName(chatProject)} (${chatProject}). Keep this conversation about that project unless the operator explicitly asks to broaden it; use it as the repo for project tools.` } : {}), ...(focusTask === null ? {} : { context: `Current task: ${focusTask.id}. Read it with get_task before answering or proposing changes. Read its currentExecution next and bind new actions to that exact execution. Never replace the target of a prior proposal with a newer revision. Keep this turn about that task unless the operator explicitly asks to broaden it.${resultContext}${modeContext}` }), fetcher: chatFetcher, ...(options.subscriptionChatRunner === undefined ? {} : { subscriptionRunner: options.subscriptionChatRunner }), clock, evidenceRoot })
|
|
854
856
|
.then(outcome => {
|
|
855
857
|
// A turn that saved its outcome in the thread (one stopped at its deadline) is said there, once.
|
|
856
858
|
if (!outcome.ok && !("saved" in outcome && outcome.saved === true))
|
|
857
|
-
|
|
859
|
+
noteLead(who.session.csrf, "turn" in outcome ? outcome.turn : null, outcome.message);
|
|
858
860
|
endLiveTurn(opened.thread.id, outcome.ok);
|
|
859
861
|
})
|
|
860
|
-
.catch(() => {
|
|
862
|
+
.catch(() => { noteLead(who.session.csrf, null, "the turn failed unexpectedly"); endLiveTurn(opened.thread.id, false); });
|
|
861
863
|
// Accepted for the engine, not received: the receipt is written by
|
|
862
864
|
// the turn's own admission, and the status poll is where the page
|
|
863
865
|
// learns of it — the same road as after a native send.
|
|
864
866
|
if (wantsJson)
|
|
865
|
-
return respond(response, 202, "application/json", JSON.stringify({ ok: true, session:
|
|
867
|
+
return respond(response, 202, "application/json", JSON.stringify({ ok: true, session: leadSession.id, task: focusTask?.id ?? "", ...(chatProject === null ? {} : { project: chatProject }), request: requestId }));
|
|
866
868
|
return redirect(response, chatReturnWithLatest(back));
|
|
867
869
|
}
|
|
868
870
|
if (wantsJson)
|
|
@@ -1089,22 +1091,22 @@ export function createChatHandlers(runtime) {
|
|
|
1089
1091
|
liveTurns.delete(thread); }, 60_000);
|
|
1090
1092
|
live.expiry.unref();
|
|
1091
1093
|
}
|
|
1092
|
-
function
|
|
1093
|
-
if (
|
|
1094
|
-
const oldest =
|
|
1094
|
+
function noteLead(csrf, turn, message) {
|
|
1095
|
+
if (leadSaid.size >= 500) {
|
|
1096
|
+
const oldest = leadSaid.keys().next().value;
|
|
1095
1097
|
if (oldest !== undefined)
|
|
1096
|
-
|
|
1098
|
+
leadSaid.delete(oldest);
|
|
1097
1099
|
}
|
|
1098
|
-
|
|
1100
|
+
leadSaid.set(csrf, { turn, message });
|
|
1099
1101
|
}
|
|
1100
|
-
function
|
|
1101
|
-
const noted =
|
|
1102
|
+
function takeLeadNote(csrf, liveSession) {
|
|
1103
|
+
const noted = leadSaid.get(csrf);
|
|
1102
1104
|
if (noted === undefined)
|
|
1103
1105
|
return null;
|
|
1104
|
-
|
|
1106
|
+
leadSaid.delete(csrf);
|
|
1105
1107
|
if (noted.turn === null)
|
|
1106
1108
|
return noted.message;
|
|
1107
|
-
const turn = store.
|
|
1109
|
+
const turn = store.getLeadTurn(noted.turn);
|
|
1108
1110
|
return turn !== null && turn.session === liveSession ? noted.message : null;
|
|
1109
1111
|
}
|
|
1110
1112
|
/** A finished demo exchange's result, read back from the evidence its run actually stored. */
|
|
@@ -1291,9 +1293,9 @@ export function createChatHandlers(runtime) {
|
|
|
1291
1293
|
agent: `${providerName(run.provider)} on ${run.runner}`, phase: homePhaseWords(run), project: run.repo === null ? null : projectName(run.repo), since: run.startedAt,
|
|
1292
1294
|
activity: runActivityOf(store, run, now) };
|
|
1293
1295
|
});
|
|
1294
|
-
const all = workIndexPage(store, now, access, { view: "all", limit: WORK_INDEX_MAX_LIMIT });
|
|
1295
|
-
const needs = workIndexPage(store, now, access, { view: "needs-you", limit: WORK_INDEX_MAX_LIMIT });
|
|
1296
|
-
const done = workIndexPage(store, now, access, { view: "completed", limit: WORK_INDEX_MAX_LIMIT });
|
|
1296
|
+
const all = workIndexPage(store, now, access, { view: "all", limit: WORK_INDEX_MAX_LIMIT, root: evidenceRoot });
|
|
1297
|
+
const needs = workIndexPage(store, now, access, { view: "needs-you", limit: WORK_INDEX_MAX_LIMIT, root: evidenceRoot });
|
|
1298
|
+
const done = workIndexPage(store, now, access, { view: "completed", limit: WORK_INDEX_MAX_LIMIT, root: evidenceRoot });
|
|
1297
1299
|
const weekAgo = new Date(now.getTime() - 7 * 86_400_000).toISOString();
|
|
1298
1300
|
const counts = [
|
|
1299
1301
|
{ key: "working", label: "Working now", value: agents.length, href: "/work?view=running" },
|
|
@@ -1312,7 +1314,7 @@ export function createChatHandlers(runtime) {
|
|
|
1312
1314
|
return href === null ? null : { label: one.primaryAction.label, href };
|
|
1313
1315
|
})(),
|
|
1314
1316
|
}));
|
|
1315
|
-
const windows = who.via === "cookie" && store.isInstanceOperator(who.name) ? limitsView(store.providerLimits(), [], { project: projectName,
|
|
1317
|
+
const windows = who.via === "cookie" && store.isInstanceOperator(who.name) ? limitsView(store.providerLimits(), [], { project: projectName, subagent: id => `Subagent ${id}` }, now) : null;
|
|
1316
1318
|
// One line: what this person's lead is doing now and when it last acted.
|
|
1317
1319
|
const activity = leadActivity(store, who.name);
|
|
1318
1320
|
const leadTask = activity?.taskId == null ? null : store.lookupRef(activity.taskId);
|
|
@@ -1382,10 +1384,10 @@ export function createChatHandlers(runtime) {
|
|
|
1382
1384
|
"push.remove": pushRemove,
|
|
1383
1385
|
"onboarding.phone-dismiss": phoneDismiss,
|
|
1384
1386
|
"chat.config": chatConfig,
|
|
1385
|
-
"chat.mate-mint":
|
|
1386
|
-
"chat.mate-follow":
|
|
1387
|
-
"chat.mate-end":
|
|
1388
|
-
"chat.mate-stop":
|
|
1387
|
+
"chat.mate-mint": leadMint,
|
|
1388
|
+
"chat.mate-follow": leadFollow,
|
|
1389
|
+
"chat.mate-end": leadEnd,
|
|
1390
|
+
"chat.mate-stop": leadStop,
|
|
1389
1391
|
"chat.proposal.confirm": chatProposalAct("confirm"),
|
|
1390
1392
|
"chat.proposal.dismiss": chatProposalAct("dismiss"),
|
|
1391
1393
|
"coordinator.proposal.confirm": coordinatorProposalAct("confirm"),
|
|
@@ -1394,5 +1396,5 @@ export function createChatHandlers(runtime) {
|
|
|
1394
1396
|
"chat.file": chatFile,
|
|
1395
1397
|
"chat.ack-send": chatAckSend,
|
|
1396
1398
|
});
|
|
1397
|
-
return { registrations, chatFirstRun, phoneCard, chatProjectOf, beginLiveTurn, endLiveTurn,
|
|
1399
|
+
return { registrations, chatFirstRun, phoneCard, chatProjectOf, beginLiveTurn, endLiveTurn, noteLead, takeLeadNote, demoResultView, storeChatKey, forgetChatKey, sweepChatDrafts, runChatTurn, chatHomeOf, firstTasksFor, approverCandidateCount, evictOldestCandidate };
|
|
1398
1400
|
}
|
package/dist/server/chrome.js
CHANGED
|
@@ -41,7 +41,7 @@ import {} from "../store.js";
|
|
|
41
41
|
import { styleAsset } from "../style-asset.js";
|
|
42
42
|
import { spendLine } from "../summary.js";
|
|
43
43
|
import { TASK_STATUS_CSS } from "../task-status.js";
|
|
44
|
-
import {
|
|
44
|
+
import { SUBAGENT_CSS } from "../subagents-ui.js";
|
|
45
45
|
import { UPDATES_CSS } from "../toolroll-update-ui.js";
|
|
46
46
|
import { TOOLS_CSS } from "../tools-ui.js";
|
|
47
47
|
import { TRANSITIONS_CSS } from "../transitions-recipes.js";
|
|
@@ -144,7 +144,7 @@ export const INBOX_TABS_CSS = '.inbox-tabs{display:inline-flex;gap:2px;max-width
|
|
|
144
144
|
'.inbox-ask{margin:18px 0 0}.inbox-ask>h2{display:flex;align-items:baseline;gap:8px;margin:0 0 4px;font-size:15px;font-weight:600}.inbox-ask>h2 .count{font:500 12px var(--so-mono,ui-monospace,monospace);font-variant-numeric:tabular-nums;color:var(--so-muted)}.inbox-ask h3{font-size:13px;font-weight:600;margin:12px 0 4px}' +
|
|
145
145
|
'.inbox-unread{display:none;position:absolute;top:4px;right:3px;width:6px;height:6px;border-radius:50%;background:var(--so-signal)}' +
|
|
146
146
|
'@media (max-width:760px){.inbox-tabs{display:flex;width:100%}.inbox-tabs a{flex:1;justify-content:center;min-height:44px;padding:0 6px}.inbox-unread{display:block}}';
|
|
147
|
-
export const WORKSPACE_STYLE = styleAsset(STYLE + BRAND_MARK_CSS + INBOX_TABS_CSS + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + STORAGE_CSS + UPDATES_CSS + LIMITS_CSS + MONITORING_CSS + INTEGRATIONS_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS +
|
|
147
|
+
export const WORKSPACE_STYLE = styleAsset(STYLE + BRAND_MARK_CSS + INBOX_TABS_CSS + APPROVAL_RULES_CSS + SPEND_CSS + RETENTION_CSS + STORAGE_CSS + UPDATES_CSS + LIMITS_CSS + MONITORING_CSS + INTEGRATIONS_CSS + BACKUP_CSS + EXPORT_CSS + PROJECT_DELETE_CSS + POLICY_CSS + EVIDENCE_PACK_CSS + THEME_CONTROLS_CSS + CODING_CSS + CODING_SHIPPING_CSS + RECIPE_CSS + SKILLS_CSS + TOOLS_CSS + FLOWS_CSS + SUBAGENT_CSS + KITS_CSS + STARTERS_CSS + GALLERY_CSS + SSO_CSS + CREDENTIALS_CSS + REQUEST_LIMITS_CSS + PEOPLE_AUDIT_CSS + KNOWLEDGE_CSS + MODELS_CSS + CHAT_POLISH_CSS + TRANSITIONS_CSS + WORKSPACE_MOTION_CSS + ASSIGNMENT_CSS + TASK_STATUS_CSS + LEAD_CONTEXT_CSS + PULL_REQUEST_SETTINGS_CSS + CHECK_SETTINGS_CSS + DISCLOSURE_CSS + '.learning{min-width:0;overflow-wrap:anywhere}.learning .card{min-width:0}.learning code,.learning blockquote,.learning pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.learning button,.learning summary,.learning .button-link{min-height:44px}.learning button{white-space:nowrap}.learning summary{padding:12px 0;cursor:pointer}.learning form{margin:12px 0}.learning select{max-width:100%}.learning blockquote{margin:8px 0}.learning ul{padding-left:20px}');
|
|
148
148
|
/**
|
|
149
149
|
* A server-rendered region that stays current: when the page's live stream
|
|
150
150
|
* says something changed (GET /live, live-bus.ts), fetch this page's own
|
|
@@ -675,7 +675,7 @@ export const NAV_ICONS = {
|
|
|
675
675
|
*/
|
|
676
676
|
export function workToolRows(scoped = false, _offersChat = false, offersCode = false) {
|
|
677
677
|
const rows = [
|
|
678
|
-
|
|
678
|
+
// D5: coding sessions are deprecated; /code still opens this release, but nothing links to it.
|
|
679
679
|
{ key: "inbox", href: "/inbox", label: "Inbox", hint: "questions, approvals, and repairs to act on" },
|
|
680
680
|
{ key: "board", href: "/board", label: "Board", hint: "lanes by state, with the order view" },
|
|
681
681
|
{ key: "tasks", href: "/tasks", label: "Task list", hint: "everything, filterable by state" },
|