toolroll 0.9.36 → 0.9.38
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 +77 -0
- package/dist/assignment-adapters.d.ts +12 -48
- package/dist/assignment-adapters.js +33 -43
- package/dist/browser/THIRD_PARTY_NOTICES.txt +3 -3
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +5 -0
- package/dist/browser-workspace.d.ts +2 -0
- package/dist/build-review.d.ts +1 -1
- package/dist/build-review.js +2 -1
- package/dist/chat-acceptance.d.ts +1 -1
- package/dist/chat-actions.d.ts +18 -12
- package/dist/chat-actions.js +15 -65
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.js +20 -9
- package/dist/chat-decide.d.ts +18 -4
- package/dist/chat-decide.js +31 -11
- package/dist/chat-delivery-state.d.ts +12 -70
- package/dist/chat-delivery-state.js +17 -135
- package/dist/chat-delivery.js +58 -30
- package/dist/chat-flow.js +8 -6
- package/dist/chat-inbox.js +1 -1
- package/dist/coding-context.d.ts +2 -39
- package/dist/coding-context.js +2 -1
- package/dist/coding-handoff.d.ts +6 -25
- package/dist/coding-handoff.js +11 -5
- package/dist/coding-types.d.ts +2 -26
- package/dist/coding-workspace.d.ts +2 -0
- package/dist/coding-workspace.js +50 -14
- package/dist/contracts/acceptance-terms.d.ts +14 -0
- package/dist/contracts/acceptance-terms.js +16 -0
- package/dist/contracts/chat-actions.d.ts +328 -0
- package/dist/contracts/chat-actions.js +133 -0
- package/dist/contracts/chat-callback-rows.d.ts +59 -0
- package/dist/contracts/chat-callback-rows.js +46 -0
- package/dist/contracts/chat-content.d.ts +276 -0
- package/dist/contracts/chat-content.js +127 -0
- package/dist/contracts/chat-tables.d.ts +6 -0
- package/dist/contracts/chat-tables.js +136 -0
- package/dist/contracts/coding-context.d.ts +97 -0
- package/dist/contracts/coding-context.js +60 -0
- package/dist/contracts/coding-handoff.d.ts +44 -0
- package/dist/contracts/coding-handoff.js +47 -0
- package/dist/contracts/coding-workspace.d.ts +165 -0
- package/dist/contracts/coding-workspace.js +71 -0
- package/dist/contracts/contract.d.ts +7 -2
- package/dist/contracts/contract.js +21 -12
- package/dist/contracts/decision.d.ts +45 -0
- package/dist/contracts/decision.js +45 -0
- package/dist/contracts/discord-callback.d.ts +45 -0
- package/dist/contracts/discord-callback.js +34 -0
- package/dist/contracts/flow-card.d.ts +50 -0
- package/dist/contracts/flow-card.js +46 -0
- package/dist/contracts/flow-send.d.ts +119 -0
- package/dist/contracts/flow-send.js +112 -0
- package/dist/contracts/flow.d.ts +5 -0
- package/dist/contracts/flow.js +5 -0
- package/dist/contracts/gateway-tools.d.ts +374 -0
- package/dist/contracts/gateway-tools.js +94 -0
- package/dist/contracts/lead-context.d.ts +165 -0
- package/dist/contracts/lead-context.js +73 -0
- package/dist/contracts/lead-tools.d.ts +1905 -0
- package/dist/contracts/lead-tools.js +265 -0
- package/dist/contracts/memory-pass.d.ts +158 -0
- package/dist/contracts/memory-pass.js +102 -0
- package/dist/contracts/project-knowledge.d.ts +126 -0
- package/dist/contracts/project-knowledge.js +93 -0
- package/dist/contracts/project-memory.d.ts +117 -0
- package/dist/contracts/project-memory.js +51 -0
- package/dist/contracts/project-skills.d.ts +121 -0
- package/dist/contracts/project-skills.js +76 -0
- package/dist/contracts/review-findings.d.ts +46 -0
- package/dist/contracts/review-findings.js +48 -0
- package/dist/contracts/route.d.ts +484 -0
- package/dist/contracts/route.js +128 -0
- package/dist/contracts/scope.d.ts +105 -0
- package/dist/contracts/scope.js +114 -0
- package/dist/contracts/scout-report.d.ts +234 -0
- package/dist/contracts/scout-report.js +141 -0
- package/dist/contracts/slack-callback.d.ts +43 -0
- package/dist/contracts/slack-callback.js +35 -0
- package/dist/contracts/sort-answer.d.ts +83 -0
- package/dist/contracts/sort-answer.js +77 -0
- package/dist/contracts/stage-output.d.ts +127 -0
- package/dist/contracts/stage-output.js +166 -0
- package/dist/contracts/task-sizing.d.ts +24 -0
- package/dist/contracts/task-sizing.js +28 -0
- package/dist/contracts/teammate-turn.d.ts +43 -0
- package/dist/contracts/teammate-turn.js +51 -0
- package/dist/contracts/teams-callback.d.ts +14 -0
- package/dist/contracts/teams-callback.js +14 -0
- package/dist/contracts/telegram-callback.d.ts +120 -0
- package/dist/contracts/telegram-callback.js +97 -0
- package/dist/decision.d.ts +11 -20
- package/dist/decision.js +86 -167
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discord-chat.js +31 -20
- package/dist/flow-engine.js +14 -17
- package/dist/flow-items.d.ts +4 -13
- package/dist/flow-items.js +1 -15
- package/dist/flow-send.d.ts +6 -37
- package/dist/flow-send.js +18 -14
- package/dist/flow-share.js +7 -1
- package/dist/flow-sort.d.ts +7 -26
- package/dist/flow-sort.js +25 -20
- package/dist/flow-steps.js +3 -2
- package/dist/flows.d.ts +2 -4
- package/dist/flows.js +9 -5
- package/dist/invoke.js +4 -4
- package/dist/lead-context.d.ts +4 -2
- package/dist/lead-context.js +24 -17
- package/dist/mate-progress.d.ts +26 -0
- package/dist/mate-progress.js +24 -0
- package/dist/mate-tools.d.ts +4 -0
- package/dist/mate-tools.js +108 -275
- package/dist/mate.js +11 -2
- package/dist/mcp-connect.d.ts +24 -4
- package/dist/mcp-connect.js +78 -15
- package/dist/mcp.js +84 -216
- package/dist/memory-pass.d.ts +17 -42
- package/dist/memory-pass.js +54 -26
- package/dist/phase-routing.d.ts +19 -72
- package/dist/phase-routing.js +34 -164
- package/dist/project-knowledge.d.ts +16 -35
- package/dist/project-knowledge.js +42 -21
- package/dist/project-memory.d.ts +2 -28
- package/dist/project-memory.js +28 -13
- package/dist/project-skills.d.ts +16 -38
- package/dist/project-skills.js +41 -32
- package/dist/project-tools.d.ts +1 -0
- package/dist/project-tools.js +4 -4
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +4 -16
- package/dist/result-shots.d.ts +1 -1
- package/dist/result-shots.js +2 -1
- package/dist/review-switch.d.ts +3 -8
- package/dist/reviewer.d.ts +8 -12
- package/dist/reviewer.js +11 -35
- package/dist/scope.d.ts +21 -43
- package/dist/scope.js +42 -128
- package/dist/scout-report.d.ts +9 -194
- package/dist/scout-report.js +113 -264
- package/dist/scout.js +15 -11
- package/dist/serve.js +23 -5
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.js +26 -20
- package/dist/slack-state.js +2 -1
- package/dist/store.d.ts +9 -32
- package/dist/store.js +18 -15
- package/dist/structured-output.js +2 -1
- package/dist/subscription-chat.d.ts +4 -0
- package/dist/subscription-chat.js +4 -1
- package/dist/task-review-brief.d.ts +1 -7
- package/dist/task-review-brief.js +6 -6
- package/dist/task-sizing.d.ts +3 -27
- package/dist/task-sizing.js +11 -26
- package/dist/task-text.d.ts +0 -5
- package/dist/task-text.js +1 -3
- package/dist/teammate-question.js +7 -5
- package/dist/teammate-work.js +9 -5
- package/dist/teammates.d.ts +13 -64
- package/dist/teammates.js +32 -33
- package/dist/teams-chat.js +20 -11
- package/dist/telegram-flow.d.ts +9 -0
- package/dist/telegram-flow.js +44 -8
- package/dist/telegram-mate.d.ts +4 -14
- package/dist/telegram-mate.js +9 -8
- package/dist/telegram-team.d.ts +2 -19
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +94 -68
- package/dist/text-limits.d.ts +142 -1
- package/dist/text-limits.js +144 -1
- package/dist/tools-ui.d.ts +2 -1
- package/dist/tools-ui.js +7 -4
- package/package.json +17 -12
package/dist/scout.js
CHANGED
|
@@ -20,7 +20,8 @@ import { auditOf } from "./provider.js";
|
|
|
20
20
|
import { currentClaim, heartbeat } from "./claim.js";
|
|
21
21
|
import { heartbeat as runnerHeartbeat } from "./runner.js";
|
|
22
22
|
import { parseDecision } from "./decision.js";
|
|
23
|
-
import {
|
|
23
|
+
import { scoutHandbackSchema } from "./contracts/scout-report.js";
|
|
24
|
+
import { parseReport, REPORT_IMAGE_FILE, REPORT_LIMITS, REPORT_VERSION, SCOUT_OUTPUT_JSON_SCHEMA } from "./scout-report.js";
|
|
24
25
|
import { TOKEN_ENVS as TELEGRAM_TOKEN_ENVS } from "./telegram.js";
|
|
25
26
|
import { evidenceRoot, mailboxName, quarantineMailboxes, readMailbox, reportFileName, SCREENSHOT_BYTE_CAP, storeEvidence, validateScreenshotBytes, writeEvidenceFile, } from "./evidence.js";
|
|
26
27
|
import { MARKER as LEASE_MARKER } from "./worktree.js";
|
|
@@ -29,7 +30,7 @@ import { proveTreeUntouched, snapshotIgnored } from "./tree-proof.js";
|
|
|
29
30
|
import { redactSecretLines, scanForSecrets } from "./evidence.js";
|
|
30
31
|
import { CLAUDE_LIMITS } from "./scope.js";
|
|
31
32
|
import { catalogTool, projectToolsOf, toolLaunchFor } from "./project-tools.js";
|
|
32
|
-
import { researchToolsOf } from "./mcp-connect.js";
|
|
33
|
+
import { grantOf, researchToolsOf } from "./mcp-connect.js";
|
|
33
34
|
import { startScoutProxy } from "./scout-net.js";
|
|
34
35
|
import * as browserCheck from "./scout-browser.js";
|
|
35
36
|
import { invokeAgent } from "./invoke.js";
|
|
@@ -160,6 +161,7 @@ function scoutBrief(title, goal, outOfScope, mailbox, reportFile, answers, struc
|
|
|
160
161
|
`\`${reportFile}\`:`,
|
|
161
162
|
]),
|
|
162
163
|
"{",
|
|
164
|
+
` "version": ${REPORT_VERSION},`,
|
|
163
165
|
' "title": "one line",',
|
|
164
166
|
' "summary": "one paragraph the operator reads first",',
|
|
165
167
|
' "report": "the report as markdown: what you found, the evidence, the risks",',
|
|
@@ -183,7 +185,7 @@ function connectedResearch(store, taskRef, runId) {
|
|
|
183
185
|
const repo = store.refById(taskRef)?.repo ?? null;
|
|
184
186
|
if (repo === null)
|
|
185
187
|
return NO_RESEARCH;
|
|
186
|
-
return researchToolsOf(projectToolsOf(store, repo), new Set(toolLaunchFor(store, runId).tools.map(one => one.spec.name)));
|
|
188
|
+
return researchToolsOf(projectToolsOf(store, repo), new Set(toolLaunchFor(store, runId).tools.map(one => one.spec.name)), tool => grantOf(repo, tool));
|
|
187
189
|
}
|
|
188
190
|
catch {
|
|
189
191
|
return NO_RESEARCH;
|
|
@@ -325,6 +327,7 @@ async function scoutWith(store, request, outputDir) {
|
|
|
325
327
|
allowedTools: proxy === null ? [] : [...(browser === null ? SCOUT_RESEARCH_TOOLS : SCOUT_ALLOWED_TOOLS), ...research.allowed],
|
|
326
328
|
...(browser === null ? {} : { extraMcpServers: browser }),
|
|
327
329
|
readOnlyTools: research.reads,
|
|
330
|
+
...(research.withheld === undefined ? {} : { researchWithheld: research.withheld }),
|
|
328
331
|
skipPermissions: false,
|
|
329
332
|
resumeSession: null,
|
|
330
333
|
...(structured ? { jsonSchema: SCOUT_OUTPUT_JSON_SCHEMA } : {}),
|
|
@@ -484,7 +487,7 @@ async function scoutWith(store, request, outputDir) {
|
|
|
484
487
|
ok: false,
|
|
485
488
|
kind: "malformed",
|
|
486
489
|
reason: "malformed-report",
|
|
487
|
-
message: `the scout concluded, but the payload is not a report: ${parsed.problems.map(problem => problem.
|
|
490
|
+
message: `the scout concluded, but the payload is not a report: ${parsed.problems.map(problem => problem.message).join("; ")}`,
|
|
488
491
|
problems: parsed.problems,
|
|
489
492
|
};
|
|
490
493
|
}
|
|
@@ -544,7 +547,7 @@ async function scoutWith(store, request, outputDir) {
|
|
|
544
547
|
// the deliverable, and a task whose deliverable does not exist is not
|
|
545
548
|
// done.
|
|
546
549
|
try {
|
|
547
|
-
const content = Buffer.from(JSON.stringify(report, null, 2), "utf8");
|
|
550
|
+
const content = Buffer.from(JSON.stringify({ version: REPORT_VERSION, ...report }, null, 2), "utf8");
|
|
548
551
|
const key = writeEvidenceFile(root, request.runId, "report.json", content);
|
|
549
552
|
return {
|
|
550
553
|
ok: true,
|
|
@@ -630,17 +633,18 @@ function structuredHandback(structuredOutput) {
|
|
|
630
633
|
value = JSON.parse(structuredOutput);
|
|
631
634
|
}
|
|
632
635
|
catch {
|
|
633
|
-
return { kind: "invalid", problems: [{ reason: "structured-not-json", message: "the structured output is not JSON" }] };
|
|
636
|
+
return { kind: "invalid", problems: [{ reason: "structured-not-json", message: "payload: the structured output is not JSON" }] };
|
|
634
637
|
}
|
|
635
638
|
const body = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
return { kind: "invalid", problems: [{ reason: "structured-kind", message: 'the structured output must say kind "report" or "question"' }] };
|
|
639
|
+
const read = scoutHandbackSchema.shape.kind.safeParse(body["kind"]);
|
|
640
|
+
if (!read.success) {
|
|
641
|
+
return { kind: "invalid", problems: [{ reason: "structured-kind", message: `kind: must be ${scoutHandbackSchema.shape.kind.options.map(one => JSON.stringify(one)).join(" or ")}` }] };
|
|
640
642
|
}
|
|
643
|
+
const kind = read.data;
|
|
644
|
+
const inner = kind === "report" ? body["report"] : body["decision"];
|
|
641
645
|
if (inner === undefined || inner === null) {
|
|
642
646
|
const field = kind === "report" ? "report" : "decision";
|
|
643
|
-
return { kind: "invalid", problems: [{ reason: `structured-missing-${field}`, message:
|
|
647
|
+
return { kind: "invalid", problems: [{ reason: `structured-missing-${field}`, message: `${field}: required for kind "${kind}"` }] };
|
|
644
648
|
}
|
|
645
649
|
return { kind, raw: JSON.stringify(inner) };
|
|
646
650
|
}
|
package/dist/serve.js
CHANGED
|
@@ -55,6 +55,7 @@ import { addFlowTriggerTo, checkFlowTriggerNow, HOOK_PATH, pressFlowButton, rece
|
|
|
55
55
|
import { addCardToFlow, advanceFlows, cancelFlowCard, crossProjectProblem, decideFlowCard, FLOW_HREF, flowDefinitionOf, moveCardInFlow } from "./flow-engine.js";
|
|
56
56
|
import { chooseFlowCard } from "./flow-send.js";
|
|
57
57
|
import { FlowContractError, FLOW_TEMPLATES, validateFlowDefinition, withZoneNames } from "./flows.js";
|
|
58
|
+
import { stageReferenceProblems } from "./contracts/stage-output.js";
|
|
58
59
|
import { TOOL_CATALOG, addToolTo, catalogTool, discoverTools, localAppOf, projectToolsOf, removeToolFrom, secretsSetFor, setToolSecret, splitCommandLine, testToolOf } from "./project-tools.js";
|
|
59
60
|
import { changeLearning, learningView } from "./project-learning.js";
|
|
60
61
|
import { applySavedKnowledge, changeKnowledge, knowledgeView, knowledgeVersion, readKnowledgeSnapshot } from "./project-knowledge.js";
|
|
@@ -723,7 +724,7 @@ export function createDecisionServer(options) {
|
|
|
723
724
|
tools: tools.map(tool => ({ tool, secretsSet: secretsSetFor(repo, tool.spec, toolHome) })),
|
|
724
725
|
// A service that connects by signing in (or an app's own Connect) is offered only that way.
|
|
725
726
|
catalog: TOOL_CATALOG.filter(one => !names.has(one.name) && oneClickOf(one.name) === null && localConnectOf(one.name) === null),
|
|
726
|
-
connections: connectionsOf(store, repo), kit, wanted,
|
|
727
|
+
connections: connectionsOf(store, repo, toolHome), kit, wanted,
|
|
727
728
|
found: discoverTools(repo, codex, toolHome).filter(one => !names.has(one.spec.name)),
|
|
728
729
|
};
|
|
729
730
|
};
|
|
@@ -4539,14 +4540,25 @@ export function createDecisionServer(options) {
|
|
|
4539
4540
|
clearTimeout(previous.expiry);
|
|
4540
4541
|
const live = { steps: [], done: false, ok: false, listeners: previous?.listeners ?? new Set() };
|
|
4541
4542
|
liveTurns.set(thread, live);
|
|
4542
|
-
const current = () => live.steps.at(-1) ?? (live.steps.push({ tools: [], text: "" }), live.steps.at(-1));
|
|
4543
|
+
const current = () => live.steps.at(-1) ?? (live.steps.push({ tools: [], toolCalls: [], text: "" }), live.steps.at(-1));
|
|
4543
4544
|
return event => {
|
|
4544
4545
|
if (live.done)
|
|
4545
4546
|
return;
|
|
4546
4547
|
if (event.kind === "step")
|
|
4547
|
-
live.steps.push({ tools: [], text: "" });
|
|
4548
|
-
else if (event.kind === "tool")
|
|
4548
|
+
live.steps.push({ tools: [], toolCalls: [], text: "" });
|
|
4549
|
+
else if (event.kind === "tool") {
|
|
4549
4550
|
current().tools.push(event.label);
|
|
4551
|
+
current().toolCalls.push({ id: event.id, label: event.label, state: "running" });
|
|
4552
|
+
}
|
|
4553
|
+
else if (event.kind === "tool-result") {
|
|
4554
|
+
for (const step of live.steps) {
|
|
4555
|
+
const index = step.toolCalls.findIndex(tool => tool.id === event.id);
|
|
4556
|
+
if (index !== -1) {
|
|
4557
|
+
step.toolCalls[index] = { ...step.toolCalls[index], ...event.outcome };
|
|
4558
|
+
break;
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4550
4562
|
else if (event.kind === "text")
|
|
4551
4563
|
current().text = event.text;
|
|
4552
4564
|
for (const listener of live.listeners)
|
|
@@ -7186,6 +7198,10 @@ export function createDecisionServer(options) {
|
|
|
7186
7198
|
try {
|
|
7187
7199
|
drawn = JSON.parse(body.get("definition") ?? "null");
|
|
7188
7200
|
saved = validateFlowDefinition(drawn);
|
|
7201
|
+
// A {{stage.…}} this save adds must be one its zone hands on; ones the saved flow had stay.
|
|
7202
|
+
const references = stageReferenceProblems(saved, flowDefinitionOf(flow));
|
|
7203
|
+
if (references.length > 0)
|
|
7204
|
+
throw new FlowContractError(references.map(one => one.line));
|
|
7189
7205
|
}
|
|
7190
7206
|
catch (error) {
|
|
7191
7207
|
return answer(400, { ok: false, said: error instanceof SyntaxError ? "That flow couldn't be read." : error instanceof FlowContractError ? withZoneNames(error.lines, drawn) : error instanceof Error ? error.message : "That flow isn't valid." });
|
|
@@ -7834,7 +7850,9 @@ export function createDecisionServer(options) {
|
|
|
7834
7850
|
const origin = consoleOrigin(request.headers.host);
|
|
7835
7851
|
if (origin === null)
|
|
7836
7852
|
return back("Connect tools from this computer (localhost) or from your https address.");
|
|
7837
|
-
|
|
7853
|
+
// Reconnect read-only (from a connected service's Tools entry): the same sign-in, asking only to read.
|
|
7854
|
+
const readOnly = also === null && body.get("access") === "read";
|
|
7855
|
+
const started = await startConnect({ service: service.id, repo: target, by: who.name, origin, kit: also === null ? kit : null, template, readOnly }, options.connectFetch ?? fetch);
|
|
7838
7856
|
if (!started.ok)
|
|
7839
7857
|
return back(started.said);
|
|
7840
7858
|
for (const [key, visit] of connectVisits)
|
package/dist/setup-guide.d.ts
CHANGED
|
@@ -15,10 +15,10 @@ export declare function detectPreparation(repo: string): {
|
|
|
15
15
|
} | null;
|
|
16
16
|
/** Browser installation may write only this known file inside its project. */
|
|
17
17
|
export declare function previewProjectInstructions(repo: string): {
|
|
18
|
-
content?: never;
|
|
19
18
|
ok: false;
|
|
20
19
|
message: string;
|
|
21
20
|
plan?: never;
|
|
21
|
+
content?: never;
|
|
22
22
|
fingerprint?: never;
|
|
23
23
|
installed?: never;
|
|
24
24
|
} | {
|
|
@@ -30,10 +30,10 @@ export declare function previewProjectInstructions(repo: string): {
|
|
|
30
30
|
installed: boolean;
|
|
31
31
|
};
|
|
32
32
|
export declare function addProjectInstructions(repo: string, fingerprint: string): {
|
|
33
|
-
content?: never;
|
|
34
33
|
ok: false;
|
|
35
34
|
message: string;
|
|
36
35
|
plan?: never;
|
|
36
|
+
content?: never;
|
|
37
37
|
fingerprint?: never;
|
|
38
38
|
installed?: never;
|
|
39
39
|
} | import("./skills.js").InstallResult;
|
package/dist/slack-chat.js
CHANGED
|
@@ -11,6 +11,9 @@ import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
|
|
|
11
11
|
import { renderReply } from "./reply-shape.js";
|
|
12
12
|
import {} from "./mate-doors.js";
|
|
13
13
|
import { SlackState, slackHash, } from "./slack-state.js";
|
|
14
|
+
import { ChatDeliveryError, partContent } from "./chat-delivery-state.js";
|
|
15
|
+
import { readChatPart } from "./contracts/chat-content.js";
|
|
16
|
+
import { readSlackBlockActions, readSlackButton, SLACK_LINK_ACTION } from "./contracts/slack-callback.js";
|
|
14
17
|
import { object, slackId, slackTs, slackMember, SlackError, uploadSlackBytes, } from "./slack-api.js";
|
|
15
18
|
import { resultImageFileName, safeResultImageCaption, verifyResultImage, } from "./chat-evidence.js";
|
|
16
19
|
import {} from "./store.js";
|
|
@@ -70,25 +73,23 @@ export function receiveSlack(state, identity, type, raw, now) {
|
|
|
70
73
|
id = slackHash(`${identity.installation}:event:${body.event_id}`);
|
|
71
74
|
}
|
|
72
75
|
else if (type === "interactive" && body.type === "block_actions") {
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
76
|
+
const interaction = readSlackBlockActions(body);
|
|
77
|
+
if (!interaction.ok || interaction.value.container.type !== "message")
|
|
75
78
|
return false;
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
!/^(?:toolroll|standing_orders)_(confirm|dismiss|yes|cancel|flow_approve|flow_edit|flow_send_back|flow_choose_[0-3]|flow_note_(?:yes|no)|question_choice|question_words)$/.test(String(action.action_id)) ||
|
|
80
|
-
typeof action.value !== "string" ||
|
|
81
|
-
!/^[a-f0-9]{32}$/.test(action.value) ||
|
|
82
|
-
!slackTs(action.action_ts) ||
|
|
83
|
-
container.type !== "message")
|
|
79
|
+
const { actions, container, user } = interaction.value;
|
|
80
|
+
// A link button opens its page itself: nothing to do, and nothing to say.
|
|
81
|
+
if (actions.length === 1 && SLACK_LINK_ACTION.test(actions[0].action_id ?? ""))
|
|
84
82
|
return false;
|
|
85
|
-
member =
|
|
83
|
+
member = user.id;
|
|
86
84
|
channel = container.channel_id;
|
|
87
85
|
ts = container.message_ts;
|
|
88
|
-
thread =
|
|
86
|
+
thread = interaction.value.message?.thread_ts ?? ts;
|
|
89
87
|
kind = "action";
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
// A button that isn't one Toolroll made is answered with why, once the tap is proved to be the paired person's below.
|
|
89
|
+
const button = readSlackButton(interaction.value);
|
|
90
|
+
payload = button.ok ? { token: button.value.value } : { problem: button.issues.map(issue => issue.line).join("; ") };
|
|
91
|
+
const tapped = actions[0];
|
|
92
|
+
id = slackHash(`${identity.installation}:action:${member}:${ts}:${tapped?.value ?? ""}:${tapped?.action_ts ?? ""}`);
|
|
92
93
|
}
|
|
93
94
|
else
|
|
94
95
|
return false;
|
|
@@ -101,9 +102,10 @@ export function receiveSlack(state, identity, type, raw, now) {
|
|
|
101
102
|
// A channel or private group is a room: accepted only while it follows a
|
|
102
103
|
// conversation, or for the `/team` words that make it follow one.
|
|
103
104
|
const isRoom = !slackId(channel, "D");
|
|
105
|
+
const words = "text" in payload ? payload.text ?? "" : "";
|
|
104
106
|
// v89: a channel that feeds a flow takes anyone's message (as a card, with no say over anything); "flow 12" connects one.
|
|
105
|
-
const inbox = isRoom && kind === "message" ? channelInbox(state.store, "slack", identity.installation, String(channel),
|
|
106
|
-
const roomish = isRoom && (state.room(identity.installation, String(channel)) !== null || (kind === "message" && roomCommand(
|
|
107
|
+
const inbox = isRoom && kind === "message" ? channelInbox(state.store, "slack", identity.installation, String(channel), words) : { watched: false, command: false };
|
|
108
|
+
const roomish = isRoom && (state.room(identity.installation, String(channel)) !== null || (kind === "message" && roomCommand(words) !== null) || inbox.watched || inbox.command);
|
|
107
109
|
if (isRoom && !roomish)
|
|
108
110
|
return false;
|
|
109
111
|
const binding = state.bindingFor(identity.installation, member);
|
|
@@ -124,7 +126,7 @@ export function receiveSlack(state, identity, type, raw, now) {
|
|
|
124
126
|
member,
|
|
125
127
|
ts,
|
|
126
128
|
thread,
|
|
127
|
-
payload
|
|
129
|
+
payload,
|
|
128
130
|
created: now.toISOString(),
|
|
129
131
|
});
|
|
130
132
|
}
|
|
@@ -227,7 +229,7 @@ export async function deliverSlackPart(options) {
|
|
|
227
229
|
const repos = await access(options, binding, session?.ceilingDigest);
|
|
228
230
|
if (event.kind === "notice" && options.canNotify?.() === false)
|
|
229
231
|
return false;
|
|
230
|
-
const content =
|
|
232
|
+
const content = partContent(row.payload);
|
|
231
233
|
const destination = content.channel ?? binding.channel;
|
|
232
234
|
let text = content.text, buttons = [];
|
|
233
235
|
if (content.task &&
|
|
@@ -435,9 +437,13 @@ export async function deliverSlackPart(options) {
|
|
|
435
437
|
return true;
|
|
436
438
|
}
|
|
437
439
|
catch (error) {
|
|
440
|
+
if (error instanceof ChatDeliveryError && error.permanent) {
|
|
441
|
+
state.prepare("UPDATE chat_part SET state='dropped',next_at=NULL,problem=? WHERE id=?").run(error.message, row.id);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
438
444
|
// No permission to upload files here: one plain line instead of the result's screenshots.
|
|
439
|
-
const
|
|
440
|
-
if (content.image && content.shot && error instanceof SlackError && ["missing_scope", "no_permission"].includes(error.code)) {
|
|
445
|
+
const saved = readChatPart(row.payload), content = saved.ok ? saved.value : null;
|
|
446
|
+
if (content !== null && content.image && content.shot && error instanceof SlackError && ["missing_scope", "no_permission"].includes(error.code)) {
|
|
441
447
|
refuseResultShots(state, store, row, content, "Slack", nowOf(options));
|
|
442
448
|
return true;
|
|
443
449
|
}
|
package/dist/slack-state.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Slack compatibility names for the shared durable transport state. */
|
|
2
|
-
import { ChatState
|
|
2
|
+
import { ChatState } from "./chat-delivery-state.js";
|
|
3
|
+
import { chatSchema, chatTables } from "./contracts/chat-tables.js";
|
|
3
4
|
export { chatHash as slackHash } from "./chat-delivery-state.js";
|
|
4
5
|
export const SLACK_SCHEMA = chatSchema("slack"), SLACK_TABLES = chatTables("slack");
|
|
5
6
|
export class SlackState extends ChatState {
|
package/dist/store.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DecisionOption } from "./contracts/decision.js";
|
|
2
|
+
import type { FlowCardChange, FlowCardFields } from "./contracts/flow-card.js";
|
|
1
3
|
import { type Actor } from "./actor.js";
|
|
2
4
|
import { type CriterionMatrixRow, type CriterionJudgementWord } from "./proof.js";
|
|
3
5
|
import { type ExecutionProfile, type ChainEntry, type UnattendedPermissionMode, type AcceptanceCriterion } from "./scope.js";
|
|
@@ -764,24 +766,9 @@ export type FlowCardSource = {
|
|
|
764
766
|
binding: number;
|
|
765
767
|
};
|
|
766
768
|
};
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
flow: number;
|
|
770
|
-
title: string;
|
|
771
|
-
description: string | null;
|
|
772
|
-
stage: string;
|
|
773
|
-
entry: number;
|
|
774
|
-
state: "active" | "done" | "cancelled";
|
|
775
|
-
task: string | null;
|
|
776
|
-
primaryTask: string | null;
|
|
777
|
-
note: string | null;
|
|
778
|
-
waiting: string | null;
|
|
779
|
-
outputs: Record<string, string>;
|
|
780
|
-
createdBy: string;
|
|
781
|
-
createdAt: string;
|
|
782
|
-
updatedAt: string;
|
|
769
|
+
/** A flow card as read (src/contracts/flow-card.ts), and where it came from. */
|
|
770
|
+
export type FlowCardRow = FlowCardFields & {
|
|
783
771
|
source: FlowCardSource | null;
|
|
784
|
-
owner: string | null;
|
|
785
772
|
};
|
|
786
773
|
/** One line of a card's discussion: a comment (and whom it @mentioned), or who became its owner. */
|
|
787
774
|
export type FlowCommentRow = {
|
|
@@ -1361,13 +1348,8 @@ export type PushPair = {
|
|
|
1361
1348
|
lastError: string | null;
|
|
1362
1349
|
acceptedAt: string | null;
|
|
1363
1350
|
};
|
|
1364
|
-
/** One option of a decision. `reversible` is a field so a scheduler can refuse to auto-apply. */
|
|
1365
|
-
export type DecisionOption
|
|
1366
|
-
id: string;
|
|
1367
|
-
label: string;
|
|
1368
|
-
consequence: string;
|
|
1369
|
-
reversible: boolean;
|
|
1370
|
-
};
|
|
1351
|
+
/** One option of a decision (src/contracts/decision.ts). `reversible` is a field so a scheduler can refuse to auto-apply. */
|
|
1352
|
+
export type { DecisionOption };
|
|
1371
1353
|
/** The judgement call an agent refused to guess at (§7). Identity = its run. */
|
|
1372
1354
|
export type Decision = {
|
|
1373
1355
|
id: number;
|
|
@@ -6389,13 +6371,9 @@ export declare class Store {
|
|
|
6389
6371
|
}, now: Date): boolean;
|
|
6390
6372
|
/** What a card's current zone is doing: its task, the card's main work, what it waits on, what zones reported. */
|
|
6391
6373
|
/** Read and written in one transaction, so a pass changing one field never writes back another pass's older task or waiting. */
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
waiting?: string | null;
|
|
6396
|
-
outputs?: Record<string, string>;
|
|
6397
|
-
state?: "done" | "cancelled";
|
|
6398
|
-
}, now: Date): void;
|
|
6374
|
+
/** Outputs are written only when they change (src/contracts/stage-output.ts), never over a row a newer Toolroll wrote;
|
|
6375
|
+
* a zone's attachment stays only while its output does. */
|
|
6376
|
+
updateFlowCard(id: number, change: FlowCardChange, now: Date): void;
|
|
6399
6377
|
flowEvents(card: number): {
|
|
6400
6378
|
fromStage: string | null;
|
|
6401
6379
|
toStage: string;
|
|
@@ -8216,4 +8194,3 @@ export type VerifiedAuthor = string & {
|
|
|
8216
8194
|
readonly __verifiedAuthor: unique symbol;
|
|
8217
8195
|
};
|
|
8218
8196
|
export declare function verifiedAuthor(name: string): VerifiedAuthor;
|
|
8219
|
-
export {};
|
package/dist/store.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { chatSchema, chatTables } from "./chat-
|
|
2
|
-
import { SLACK_SCHEMA, SLACK_TABLES } from "./slack-state.js";
|
|
1
|
+
import { chatSchema, chatTables } from "./contracts/chat-tables.js";
|
|
3
2
|
import { assessmentFromSavedEvidence, verificationEvidence } from "./verification-evidence.js";
|
|
4
3
|
import { LEARNING_SCHEMA, queueLearning } from "./project-learning.js";
|
|
5
4
|
import { SKILLS_SCHEMA } from "./project-skills.js";
|
|
@@ -8,6 +7,7 @@ import { MEMORY_SCHEMA } from "./project-memory.js";
|
|
|
8
7
|
import { MODELS_SCHEMA } from "./model-catalog.js";
|
|
9
8
|
import { validateTaskText } from "./task-text.js";
|
|
10
9
|
import { flowDefinitionForStore, flowDefinitionFromStore } from "./flows.js";
|
|
10
|
+
import { cardOutputsForStore, cardOutputsFromStore } from "./contracts/stage-output.js";
|
|
11
11
|
import { chatControlHref, chatResultHref } from "./chat-controls.js";
|
|
12
12
|
import { actorLabel, currentActor, leadSecretMatches, mintLeadToken, parseLeadToken } from "./actor.js";
|
|
13
13
|
import { scanForSecrets } from "./evidence.js";
|
|
@@ -687,18 +687,11 @@ function readFlowStepRunRow(row) {
|
|
|
687
687
|
durationMs: number("duration_ms"), exitCode: number("exit_code"), result: optional("result"), log: optional("log"), decisionJson: optional("decision_json") };
|
|
688
688
|
}
|
|
689
689
|
function readFlowCardRow(row) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const parsed = JSON.parse(String(row["outputs_json"] ?? "{}"));
|
|
693
|
-
if (parsed !== null && typeof parsed === "object")
|
|
694
|
-
outputs = Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string"));
|
|
695
|
-
}
|
|
696
|
-
catch {
|
|
697
|
-
outputs = {};
|
|
698
|
-
}
|
|
690
|
+
// What finished zones handed on (src/contracts/stage-output.ts): a row saved before it carried a version reads as it always did.
|
|
691
|
+
const { outputs, attached } = cardOutputsFromStore(String(row["outputs_json"] ?? "{}"));
|
|
699
692
|
const optional = (key) => row[key] === null || row[key] === undefined ? null : String(row[key]);
|
|
700
693
|
return { id: Number(row["id"]), flow: Number(row["flow"]), title: String(row["title"]), description: optional("description"), stage: String(row["stage"]), entry: Number(row["entry"]),
|
|
701
|
-
state: String(row["state"]), task: optional("task"), primaryTask: optional("primary_task"), note: optional("note"), waiting: optional("waiting"), outputs,
|
|
694
|
+
state: String(row["state"]), task: optional("task"), primaryTask: optional("primary_task"), note: optional("note"), waiting: optional("waiting"), outputs, attached,
|
|
702
695
|
createdBy: String(row["created_by"]), createdAt: String(row["created_at"]), updatedAt: String(row["updated_at"]), source: readFlowCardSource(row["source_json"]), owner: optional("owner") };
|
|
703
696
|
}
|
|
704
697
|
function readFlowCardSource(value) {
|
|
@@ -3950,7 +3943,7 @@ function initializeStore(db, file) {
|
|
|
3950
3943
|
throw new Error(`${file}: shared action history has an unknown shape; refusing to recreate it`);
|
|
3951
3944
|
}
|
|
3952
3945
|
if (preflight !== null && Math.abs(preflight) >= 67) {
|
|
3953
|
-
for (const table of
|
|
3946
|
+
for (const table of chatTables("slack"))
|
|
3954
3947
|
if (!tableExists(db, table))
|
|
3955
3948
|
throw new Error(`${file}: Slack history is missing; refusing to recreate receipts`);
|
|
3956
3949
|
}
|
|
@@ -3971,7 +3964,7 @@ function initializeStore(db, file) {
|
|
|
3971
3964
|
db.exec(MEMORY_SCHEMA);
|
|
3972
3965
|
db.exec(MODELS_SCHEMA);
|
|
3973
3966
|
db.exec(SKILLS_SCHEMA);
|
|
3974
|
-
db.exec(
|
|
3967
|
+
db.exec(chatSchema("slack"));
|
|
3975
3968
|
db.exec(chatSchema("discord"));
|
|
3976
3969
|
db.exec(chatSchema("teams"));
|
|
3977
3970
|
db.exec(TEAM_SCHEMA);
|
|
@@ -19362,12 +19355,22 @@ export class Store {
|
|
|
19362
19355
|
}
|
|
19363
19356
|
/** What a card's current zone is doing: its task, the card's main work, what it waits on, what zones reported. */
|
|
19364
19357
|
/** Read and written in one transaction, so a pass changing one field never writes back another pass's older task or waiting. */
|
|
19358
|
+
/** Outputs are written only when they change (src/contracts/stage-output.ts), never over a row a newer Toolroll wrote;
|
|
19359
|
+
* a zone's attachment stays only while its output does. */
|
|
19365
19360
|
updateFlowCard(id, change, now) {
|
|
19366
19361
|
this.transact(() => {
|
|
19367
19362
|
const card = this.getFlowCard(id);
|
|
19368
19363
|
if (card === null)
|
|
19369
19364
|
return;
|
|
19370
|
-
this.db.prepare("UPDATE flow_card SET task = ?, primary_task = ?, waiting = ?,
|
|
19365
|
+
this.db.prepare("UPDATE flow_card SET task = ?, primary_task = ?, waiting = ?, state = ?, updated_at = ? WHERE id = ?").run(change.task === undefined ? card.task : change.task, change.primaryTask ?? card.primaryTask, change.waiting === undefined ? card.waiting : change.waiting, change.state ?? card.state, now.toISOString(), id);
|
|
19366
|
+
if (change.outputs === undefined && change.attached === undefined)
|
|
19367
|
+
return;
|
|
19368
|
+
const saved = this.db.prepare("SELECT outputs_json FROM flow_card WHERE id = ?").get(id);
|
|
19369
|
+
if (cardOutputsFromStore(String(saved?.["outputs_json"] ?? "{}")).newer)
|
|
19370
|
+
return;
|
|
19371
|
+
const outputs = change.outputs ?? card.outputs;
|
|
19372
|
+
const attached = { ...Object.fromEntries(Object.entries(card.attached).filter(([zone]) => outputs[zone] === card.outputs[zone])), ...change.attached };
|
|
19373
|
+
this.db.prepare("UPDATE flow_card SET outputs_json = ? WHERE id = ?").run(cardOutputsForStore(outputs, attached), id);
|
|
19371
19374
|
});
|
|
19372
19375
|
}
|
|
19373
19376
|
flowEvents(card) {
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
* subject to the phase's strict parser.
|
|
9
9
|
*/
|
|
10
10
|
import { redactSecretLines, scanForSecrets, storeEvidence } from "./evidence.js";
|
|
11
|
+
import { TEXT_LIMITS } from "./text-limits.js";
|
|
11
12
|
export const STRUCTURED_REPAIR_ATTEMPTS = 2;
|
|
12
13
|
export const STRUCTURED_REPAIR_MAX_TURNS = 4;
|
|
13
14
|
export const STRUCTURED_REPAIR_TIMEOUT_MS = 5 * 60_000;
|
|
14
15
|
/** Native parsing counts UTF-16 code units. A shorter writing target keeps
|
|
15
16
|
* notes concise without imposing a second, stricter transport contract. */
|
|
16
|
-
export const REVIEW_OUTPUT_LIMITS = { comments: 40, criteria: 12, note:
|
|
17
|
+
export const REVIEW_OUTPUT_LIMITS = { comments: 40, criteria: 12, note: TEXT_LIMITS.reviewNote, path: TEXT_LIMITS.reviewPath };
|
|
17
18
|
export const REVIEW_NOTE_CODE_POINTS = Math.floor(REVIEW_OUTPUT_LIMITS.note / 2);
|
|
18
19
|
export const REVIEW_NOTE_GUIDANCE = `Keep each comment and criterion note to one concise finding and its evidence. Aim for ${REVIEW_NOTE_CODE_POINTS} characters or fewer; the hard limit is ${REVIEW_OUTPUT_LIMITS.note} UTF-16 units. Preserve the conclusion and any needed provenance; omit repeated rubric text.`;
|
|
19
20
|
/** Strip only unambiguous, whole-payload wrappers. */
|
|
@@ -9,6 +9,10 @@ export type SubscriptionMateRequest = {
|
|
|
9
9
|
dataDocument: string;
|
|
10
10
|
history: readonly MateHistoryMessage[];
|
|
11
11
|
tools: readonly MateToolSchema[];
|
|
12
|
+
/** A structured answer of another shape (the memory pass's verdict): this JSON Schema is the harness's
|
|
13
|
+
* `--json-schema` (Codex's `--output-schema`) in place of the mate envelope, and its JSON text comes back as the
|
|
14
|
+
* answer's text, unread, for the caller's own schema to parse. */
|
|
15
|
+
outputSchema?: Record<string, unknown>;
|
|
12
16
|
timeoutMs: number;
|
|
13
17
|
/** The turn's own deadline: aborting it ends the harness's whole process group. */
|
|
14
18
|
signal?: AbortSignal;
|
|
@@ -191,7 +191,7 @@ export async function performSubscriptionMateRequest(request, runner = run) {
|
|
|
191
191
|
try {
|
|
192
192
|
const prompt = composeSubscriptionMatePrompt(request);
|
|
193
193
|
const streaming = request.provider !== "codex-subscription" && request.onText !== undefined;
|
|
194
|
-
const schema = outputSchema(request.tools);
|
|
194
|
+
const schema = request.outputSchema ?? outputSchema(request.tools);
|
|
195
195
|
let command;
|
|
196
196
|
let args;
|
|
197
197
|
if (request.provider === "codex-subscription") {
|
|
@@ -271,6 +271,9 @@ export async function performSubscriptionMateRequest(request, runner = run) {
|
|
|
271
271
|
const output = request.provider === "codex-subscription" ? codexOutput(result.stdout) : claudeOutput(streaming ? lastResultLine(result.stdout) : result.stdout);
|
|
272
272
|
if (output.text === null)
|
|
273
273
|
return { ok: false, problem: "malformed-reply" };
|
|
274
|
+
if (request.outputSchema !== undefined) {
|
|
275
|
+
return { ok: true, answer: { text: output.text, calls: [], tokensIn: output.tokensIn, tokensOut: output.tokensOut, reportedCostMicrousd: null } };
|
|
276
|
+
}
|
|
274
277
|
return parseSubscriptionMateAnswer(output.text, output);
|
|
275
278
|
}
|
|
276
279
|
finally {
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
/** A read-only packet of saved result facts. Never opens a worktree or emits logs, patches or saved shell commands. */
|
|
2
2
|
import type { Store } from './store.js';
|
|
3
3
|
import { type AssignmentAccess } from './assignment.js';
|
|
4
|
-
import { type FindingSeverity } from './review-
|
|
4
|
+
import { type BuildFinding as Finding, type FindingSeverity } from './contracts/review-findings.js';
|
|
5
5
|
declare const MISSING: 'not recorded';
|
|
6
6
|
type Missing = typeof MISSING;
|
|
7
|
-
type Finding = {
|
|
8
|
-
severity: FindingSeverity;
|
|
9
|
-
file: string;
|
|
10
|
-
line: number;
|
|
11
|
-
scenario: string;
|
|
12
|
-
};
|
|
13
7
|
export type ReviewBrief = {
|
|
14
8
|
header: {
|
|
15
9
|
task: string;
|
|
@@ -2,13 +2,13 @@ import { assignmentChecksForRun, assignmentOf } from './assignment.js';
|
|
|
2
2
|
import { assignmentPresentationOf } from './assignment-presentation.js';
|
|
3
3
|
import { readVerifiedArtifact, readVerifiedProofForRun, reportShotsOf, scanForSecrets } from './evidence.js';
|
|
4
4
|
import { SCREENSHOT_CAPTURE, structuredHandoffView, terminalDiffView } from './result-evidence-readers.js';
|
|
5
|
+
import { FINDING_SEVERITIES } from './contracts/review-findings.js';
|
|
5
6
|
import { buildReviewOf } from './review-switch.js';
|
|
6
7
|
import { parseReport } from './scout-report.js';
|
|
7
8
|
import { sanitizeTranscriptLine } from './live.js';
|
|
8
9
|
import { redactSecretAssignments } from './builder.js';
|
|
9
10
|
import { parseExecutionPlanDocument } from './plan.js';
|
|
10
11
|
const MISSING = 'not recorded';
|
|
11
|
-
const SEVERITIES = ['HIGH', 'MEDIUM', 'LOW'];
|
|
12
12
|
/** Redact before shortening so a clipped credential cannot escape recognition. Apply to every data string in both formats. */
|
|
13
13
|
function safeText(value, secrets, cap = 300) {
|
|
14
14
|
if (typeof value !== 'string' || !value.trim())
|
|
@@ -65,11 +65,11 @@ export function taskReviewBrief(store, task, root, now, access, options = {}) {
|
|
|
65
65
|
...Object.entries(progress?.suites ?? {}).filter(([, suite]) => suite.state === 'failed').map(([name, suite]) => `${name}: ${suite.failed} failed`),
|
|
66
66
|
])] : [];
|
|
67
67
|
const review = run === null ? null : buildReviewOf(store, run.id);
|
|
68
|
-
const findings = review === null ? [] : [...review.high, ...review.followUps].filter(one => one &&
|
|
69
|
-
const counts = Object.fromEntries(
|
|
70
|
-
const shown =
|
|
68
|
+
const findings = review === null ? [] : [...review.high, ...review.followUps].filter(one => one && FINDING_SEVERITIES.includes(one.severity));
|
|
69
|
+
const counts = Object.fromEntries(FINDING_SEVERITIES.map(severity => [severity, findings.filter(one => one.severity === severity).length]));
|
|
70
|
+
const shown = FINDING_SEVERITIES.flatMap(severity => severity === 'LOW' && !options.all ? [] : findings.filter(one => one.severity === severity))
|
|
71
71
|
.slice(0, 12).map(one => ({ severity: one.severity, file: safe(one.file), line: Number.isSafeInteger(one.line) ? one.line : 0, scenario: safe(one.scenario) }));
|
|
72
|
-
const omitted = Object.fromEntries(
|
|
72
|
+
const omitted = Object.fromEntries(FINDING_SEVERITIES.map(severity => [severity, counts[severity] - shown.filter(one => one.severity === severity).length]));
|
|
73
73
|
const proof = run === null ? null : readVerifiedProofForRun(store, root, run.id);
|
|
74
74
|
const shots = artifacts.filter(one => one.kind === 'screenshot');
|
|
75
75
|
const reportShots = report?.ok && run !== null ? reportShotsOf(artifacts, root, run.id, report.report.images) : [];
|
|
@@ -152,7 +152,7 @@ export function renderReviewBrief(brief) {
|
|
|
152
152
|
else {
|
|
153
153
|
lines.push(`Automatic review: ${brief.review.state}${brief.review.reason === MISSING ? '' : ` · ${brief.review.reason}`}`);
|
|
154
154
|
lines.push(...brief.review.findings.map(one => ` ${one.severity} ${one.file}:${one.line} — ${one.scenario}`));
|
|
155
|
-
for (const severity of
|
|
155
|
+
for (const severity of FINDING_SEVERITIES)
|
|
156
156
|
if (brief.review.omitted[severity])
|
|
157
157
|
lines.push(` ${severity}: ${brief.review.omitted[severity]} more (${brief.review.counts[severity]} total)`);
|
|
158
158
|
if (Object.values(brief.review.counts).every(count => count === 0) && brief.review.state === 'reviewed')
|
package/dist/task-sizing.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type SizeAnswer } from "./contracts/task-sizing.js";
|
|
1
2
|
import { run, type ExecResult } from "./exec.js";
|
|
2
|
-
import { type
|
|
3
|
+
import { type TaskSizing } from "./phase-routing.js";
|
|
3
4
|
import type { Store } from "./store.js";
|
|
4
5
|
/** The classifier's whole budget, from asking to answer. */
|
|
5
6
|
export declare const SIZING_BUDGET_MS = 5000;
|
|
@@ -11,11 +12,7 @@ export type SizingInput = {
|
|
|
11
12
|
outOfScope?: string | null | undefined;
|
|
12
13
|
touches?: readonly string[] | undefined;
|
|
13
14
|
};
|
|
14
|
-
export type SizeAnswer
|
|
15
|
-
size: TaskSize;
|
|
16
|
-
risky: boolean;
|
|
17
|
-
reason: string;
|
|
18
|
-
};
|
|
15
|
+
export type { SizeAnswer } from "./contracts/task-sizing.js";
|
|
19
16
|
/** One classifier: null (or a throw) is "no answer"; the signal ends at the budget. */
|
|
20
17
|
export type Sizer = (input: SizingInput, signal: AbortSignal) => Promise<SizeAnswer | null>;
|
|
21
18
|
/** Structural or broad work — the description signal planning has always read. */
|
|
@@ -27,26 +24,6 @@ export declare function planningSignals(input: SizingInput): boolean;
|
|
|
27
24
|
export declare function heuristicSizing(input: SizingInput, why?: string | null): TaskSizing;
|
|
28
25
|
/** What the classifier reads: the task as data, bounded, keys blanked. */
|
|
29
26
|
export declare function sizingText(input: SizingInput): string;
|
|
30
|
-
export declare const SIZING_SCHEMA: {
|
|
31
|
-
readonly type: "object";
|
|
32
|
-
readonly properties: {
|
|
33
|
-
readonly size: {
|
|
34
|
-
readonly type: "string";
|
|
35
|
-
readonly enum: readonly ["small", "medium", "large"];
|
|
36
|
-
};
|
|
37
|
-
readonly risky: {
|
|
38
|
-
readonly type: "boolean";
|
|
39
|
-
};
|
|
40
|
-
readonly reason: {
|
|
41
|
-
readonly type: "string";
|
|
42
|
-
readonly maxLength: 160;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
readonly required: readonly ["size", "risky", "reason"];
|
|
46
|
-
readonly additionalProperties: false;
|
|
47
|
-
};
|
|
48
|
-
/** Read an answer back: only the three sizes, a boolean, and a short reason. */
|
|
49
|
-
export declare function readSizeAnswer(value: unknown): SizeAnswer | null;
|
|
50
27
|
type CommandRunner = (file: string, args: readonly string[], options: Parameters<typeof run>[2]) => Promise<ExecResult>;
|
|
51
28
|
/** A small Claude model on this computer's sign-in: no tools, no MCP servers, no repository, credential keys stripped.
|
|
52
29
|
* The abort ends it: a late answer never keeps a command (or its process) waiting. */
|
|
@@ -75,4 +52,3 @@ export declare function filingSizerInstalled(): boolean;
|
|
|
75
52
|
export declare function refineFiledSizing(store: Store, taskId: string, input: SizingInput, followPlanning: boolean, clock?: () => Date): Promise<void> | null;
|
|
76
53
|
/** Wait for every classification still in flight (each is bounded by the budget). */
|
|
77
54
|
export declare function settleSizings(): Promise<void>;
|
|
78
|
-
export {};
|