toolroll 0.9.54 → 0.9.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +29 -12
- package/dist/action-ledger.d.ts +2 -4
- package/dist/action-ledger.js +2 -10
- package/dist/agent-onboard.js +4 -1
- package/dist/approval-policy.d.ts +5 -5
- package/dist/approval-policy.js +4 -4
- package/dist/approval-rules-ui.d.ts +3 -2
- package/dist/approval-rules-ui.js +17 -22
- package/dist/assignment-ui.d.ts +3 -2
- package/dist/assignment-ui.js +4 -11
- package/dist/backup-ui.d.ts +2 -1
- package/dist/backup-ui.js +16 -27
- package/dist/brand-mark.d.ts +3 -2
- package/dist/brand-mark.js +4 -4
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +25 -25
- package/dist/browser-shell.d.ts +2 -1
- package/dist/browser-shell.js +9 -9
- package/dist/browser-workspace.d.ts +14 -15
- package/dist/browser-workspace.js +0 -3
- package/dist/budget-alerts.js +3 -3
- package/dist/chat-actions.d.ts +23 -18
- package/dist/chat-actions.js +110 -88
- package/dist/chat-ask.d.ts +2 -2
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.d.ts +15 -15
- package/dist/chat-channel.js +20 -20
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.js +1 -1
- package/dist/chat-delivery-state.js +2 -2
- package/dist/chat-delivery.d.ts +2 -2
- package/dist/chat-delivery.js +27 -34
- package/dist/chat-flow.js +3 -3
- package/dist/chat-messages.d.ts +1 -1
- package/dist/chat-messages.js +4 -4
- package/dist/chat-migration.d.ts +1 -1
- package/dist/chat-migration.js +11 -11
- package/dist/chat-polish.d.ts +5 -4
- package/dist/chat-polish.js +9 -10
- package/dist/chat-rooms.js +3 -3
- package/dist/chat-warmth.d.ts +2 -2
- package/dist/check-levels-ui.d.ts +2 -1
- package/dist/check-levels-ui.js +26 -53
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +15 -10
- package/dist/coding-shipping-ui.d.ts +2 -1
- package/dist/coding-shipping-ui.js +6 -6
- package/dist/coding-ui.d.ts +3 -1
- package/dist/coding-ui.js +30 -29
- 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/cli.d.ts +1 -3
- package/dist/contracts/console-api.d.ts +7 -7
- package/dist/contracts/console-api.js +13 -12
- 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/health.d.ts +1 -3
- 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/control-ui.d.ts +5 -4
- package/dist/control-ui.js +9 -34
- 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/credentials-ui.d.ts +5 -4
- package/dist/credentials-ui.js +23 -24
- package/dist/demo-chat.d.ts +3 -3
- package/dist/demo-chat.js +29 -76
- 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/discord-settings.d.ts +3 -2
- package/dist/discord-settings.js +17 -42
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/envelope.js +10 -1
- package/dist/evidence-pack.d.ts +2 -1
- package/dist/evidence-pack.js +45 -49
- package/dist/export-ui.d.ts +7 -2
- package/dist/export-ui.js +4 -10
- package/dist/export.js +7 -7
- package/dist/flow-engine.d.ts +2 -2
- package/dist/flow-engine.js +21 -21
- package/dist/flow-gallery-ui.d.ts +4 -3
- package/dist/flow-gallery-ui.js +28 -43
- package/dist/flow-insights.d.ts +1 -1
- package/dist/flow-share.js +13 -8
- package/dist/flow-starters-ui.d.ts +2 -1
- package/dist/flow-starters-ui.js +12 -15
- package/dist/flow-steps.d.ts +5 -5
- package/dist/flow-steps.js +19 -19
- package/dist/flow-triggers.d.ts +2 -1
- package/dist/flow-triggers.js +17 -15
- package/dist/flows-cli.js +1 -1
- package/dist/flows-ui.d.ts +4 -3
- package/dist/flows-ui.js +38 -49
- package/dist/flows.js +21 -20
- package/dist/guides.js +112 -6
- package/dist/html.d.ts +76 -0
- package/dist/html.js +173 -0
- package/dist/integrations-ui.d.ts +2 -1
- package/dist/integrations-ui.js +19 -23
- package/dist/invoke.js +1 -1
- package/dist/kits-ui.d.ts +5 -4
- package/dist/kits-ui.js +22 -24
- package/dist/kits.d.ts +12 -12
- package/dist/kits.js +31 -31
- package/dist/knowledge-ui.d.ts +6 -5
- package/dist/knowledge-ui.js +26 -25
- 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 +12 -2
- package/dist/lead-context.js +15 -17
- package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
- package/dist/{mate-contract.js → lead-contract.js} +7 -6
- package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
- package/dist/{mate-doors.js → lead-doors.js} +24 -24
- package/dist/lead-follow.d.ts +5 -5
- package/dist/lead-follow.js +9 -9
- package/dist/lead-people.d.ts +5 -5
- package/dist/lead-people.js +14 -14
- package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
- package/dist/{mate-progress.js → lead-progress.js} +3 -3
- package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
- package/dist/{mate-tools.js → lead-tools.js} +109 -101
- package/dist/{mate.d.ts → lead.d.ts} +27 -27
- package/dist/{mate.js → lead.js} +102 -102
- package/dist/ledger-view.d.ts +2 -1
- package/dist/ledger-view.js +27 -29
- package/dist/limits-ui.d.ts +3 -2
- package/dist/limits-ui.js +8 -8
- package/dist/live-bus.d.ts +50 -8
- package/dist/live-bus.js +133 -50
- package/dist/mcp-connect.d.ts +1 -1
- package/dist/mcp-connect.js +3 -3
- package/dist/mcp-core.js +1 -1
- package/dist/mcp-oauth.js +35 -36
- package/dist/memory-pass.d.ts +2 -2
- package/dist/memory-pass.js +7 -7
- package/dist/models-ui.d.ts +2 -1
- package/dist/models-ui.js +25 -35
- package/dist/monitoring-ui.d.ts +4 -3
- package/dist/monitoring-ui.js +13 -26
- package/dist/notification-rows.d.ts +2 -2
- package/dist/openrouter-models.d.ts +2 -1
- package/dist/openrouter-models.js +5 -9
- package/dist/operate.d.ts +3 -3
- package/dist/operate.js +46 -41
- package/dist/people-audit-ui.d.ts +2 -1
- package/dist/people-audit-ui.js +13 -13
- package/dist/policy-ui.d.ts +3 -2
- package/dist/policy-ui.js +17 -23
- 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.d.ts +4 -3
- package/dist/project-delete-ui.js +16 -28
- 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/pull-request-ui.d.ts +2 -1
- package/dist/pull-request-ui.js +17 -32
- package/dist/recipe-ui.d.ts +8 -7
- package/dist/recipe-ui.js +39 -32
- package/dist/release-adapters.d.ts +60 -0
- package/dist/release-adapters.js +435 -0
- package/dist/release-cli.d.ts +24 -0
- package/dist/release-cli.js +181 -0
- package/dist/release-gate.d.ts +80 -0
- package/dist/release-gate.js +150 -0
- package/dist/release-links.d.ts +29 -0
- package/dist/release-links.js +102 -0
- package/dist/release.d.ts +331 -0
- package/dist/release.js +550 -0
- package/dist/reply-shape.d.ts +2 -1
- package/dist/reply-shape.js +5 -5
- package/dist/repository-context-ui.d.ts +2 -1
- package/dist/repository-context-ui.js +2 -7
- package/dist/request-budget-ui.d.ts +2 -1
- package/dist/request-budget-ui.js +16 -16
- package/dist/result-review.d.ts +2 -3
- package/dist/result-review.js +3 -9
- package/dist/retention-ui.d.ts +7 -1
- package/dist/retention-ui.js +7 -11
- 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.d.ts +11 -2
- package/dist/serve.js +105 -99
- package/dist/server/chat.d.ts +7 -10
- package/dist/server/chat.js +984 -1021
- package/dist/server/chrome.d.ts +291 -0
- package/dist/server/chrome.js +747 -0
- package/dist/server/console.css +2819 -0
- package/dist/server/flows.d.ts +2 -6
- package/dist/server/flows.js +854 -773
- package/dist/server/guards.d.ts +1 -1
- package/dist/server/guards.js +8 -5
- package/dist/server/handler-context.d.ts +1 -1
- package/dist/server/handler-context.js +1 -1
- package/dist/server/handler-registry.d.ts +8 -6
- package/dist/server/handler-registry.js +14 -2
- package/dist/server/http.d.ts +274 -0
- package/dist/server/http.js +239 -0
- package/dist/server/live.d.ts +34 -0
- package/dist/server/live.js +309 -0
- package/dist/server/pages.d.ts +2 -6
- package/dist/server/pages.js +808 -789
- package/dist/server/people-tokens.d.ts +1 -6
- package/dist/server/people-tokens.js +581 -573
- package/dist/server/remote.d.ts +4 -7
- package/dist/server/remote.js +39 -70
- package/dist/server/render-chat.d.ts +402 -0
- package/dist/server/render-chat.js +927 -0
- package/dist/server/render-pages.d.ts +535 -0
- package/dist/server/render-pages.js +1277 -0
- package/dist/server/render-people.d.ts +32 -0
- package/dist/server/render-people.js +121 -0
- package/dist/server/render-results.d.ts +744 -0
- package/dist/server/render-results.js +2237 -0
- package/dist/server/render-settings.d.ts +154 -0
- package/dist/server/render-settings.js +580 -0
- package/dist/server/render-tasks.d.ts +632 -0
- package/dist/server/render-tasks.js +2345 -0
- package/dist/server/request-context.d.ts +46 -0
- package/dist/server/request-context.js +17 -0
- package/dist/server/route-table.d.ts +25 -3
- package/dist/server/route-table.js +167 -77
- package/dist/server/runtime.d.ts +28 -20
- package/dist/server/runtime.js +7 -1
- package/dist/server/session.d.ts +138 -0
- package/dist/server/session.js +94 -0
- package/dist/server/settings.d.ts +1 -5
- package/dist/server/settings.js +2186 -2121
- package/dist/server/task-views.d.ts +11 -4
- package/dist/server/task-views.js +26 -22
- package/dist/server/tasks.d.ts +9 -9
- package/dist/server/tasks.js +1489 -1420
- package/dist/server-telemetry.d.ts +4 -3
- package/dist/server-telemetry.js +4 -8
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills-ui.d.ts +4 -3
- package/dist/skills-ui.js +14 -17
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +2 -1
- package/dist/slack-chat.d.ts +2 -2
- package/dist/slack-chat.js +6 -6
- package/dist/slack-settings.d.ts +3 -2
- package/dist/slack-settings.js +17 -42
- package/dist/spend-ui.d.ts +5 -4
- package/dist/spend-ui.js +17 -40
- package/dist/spend.d.ts +14 -14
- package/dist/spend.js +31 -31
- package/dist/sso-ui.d.ts +3 -2
- package/dist/sso-ui.js +26 -27
- package/dist/storage-ui.d.ts +2 -1
- package/dist/storage-ui.js +14 -33
- package/dist/store-baseline.d.ts +19 -0
- package/dist/store-baseline.js +2873 -0
- package/dist/store.d.ts +197 -249
- package/dist/store.js +747 -3084
- package/dist/subagent-admin.d.ts +53 -0
- package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
- package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
- package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
- package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
- package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
- package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
- package/dist/{teammate-question.js → subagent-question.js} +17 -17
- package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
- package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
- package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
- package/dist/{teammate-week.js → subagent-week.js} +23 -23
- package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
- package/dist/{teammate-work.js → subagent-work.js} +50 -50
- package/dist/subagents-ui.d.ts +22 -0
- package/dist/subagents-ui.js +137 -0
- 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 +20 -33
- package/dist/task-composer.d.ts +2 -1
- package/dist/task-composer.js +7 -7
- package/dist/task-status.d.ts +4 -3
- package/dist/task-status.js +15 -16
- package/dist/team-cli.d.ts +1 -0
- package/dist/team-cli.js +14 -2
- package/dist/team-http.d.ts +5 -9
- package/dist/team-http.js +5 -63
- package/dist/team-leads.js +13 -13
- package/dist/team-runtime.d.ts +2 -2
- package/dist/team-runtime.js +8 -8
- package/dist/team-ui.d.ts +2 -1
- package/dist/team-ui.js +2 -5
- package/dist/teams-chat.js +6 -6
- package/dist/teams-settings.d.ts +3 -2
- package/dist/teams-settings.js +20 -31
- 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-settings.d.ts +3 -2
- package/dist/telegram-settings.js +14 -25
- package/dist/telegram-status.js +1 -1
- package/dist/telegram-team.d.ts +1 -1
- package/dist/telegram-team.js +5 -5
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +9 -30
- package/dist/text-limits.d.ts +18 -18
- package/dist/text-limits.js +18 -18
- package/dist/toolroll-update-ui.d.ts +3 -2
- package/dist/toolroll-update-ui.js +35 -39
- package/dist/toolroll-update.d.ts +8 -0
- package/dist/toolroll-update.js +45 -8
- package/dist/tools-ui.d.ts +5 -2
- package/dist/tools-ui.js +31 -31
- package/dist/when-html.d.ts +11 -3
- package/dist/when-html.js +5 -6
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +10 -10
- package/package.json +1 -2
- package/dist/server/shared.d.ts +0 -3151
- package/dist/server/shared.js +0 -12001
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -20
- package/dist/teammates-ui.js +0 -166
package/dist/evidence-pack.js
CHANGED
|
@@ -15,6 +15,7 @@ import { rulesWords } from "./approval-policy.js";
|
|
|
15
15
|
import { assignmentOf } from "./assignment.js";
|
|
16
16
|
import { projectName } from "./project.js";
|
|
17
17
|
import { changedFilesOf } from "./result-completion.js";
|
|
18
|
+
import { html, htmlString, joinHtml, styleElement } from "./html.js";
|
|
18
19
|
export const EVIDENCE_PACK_FORMAT = "standing-orders/evidence-pack/v1";
|
|
19
20
|
/** How to recheck one entry's seal, for whoever reads the JSON. */
|
|
20
21
|
export const SEAL_RECIPE = 'hash = sha256(prev + "\\n" + JSON.stringify([id, at, actor, repo, taskId, runId, action, outcome, source, detail]))';
|
|
@@ -173,13 +174,12 @@ export function* ledgerExportChunks(store, range, scope, access, who, now, evide
|
|
|
173
174
|
yield `],"truncated":${JSON.stringify({ entries: moreEntries, packs: morePacks })}}`;
|
|
174
175
|
}
|
|
175
176
|
// ---- The printable page -----------------------------------------------------
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const short = (hash) => hash === null ? "—" : `<code title="${e(hash)}">${e(hash.slice(0, 12))}</code>`;
|
|
177
|
+
const when = (at) => at === null ? html `—` : html `${at.slice(0, 16).replace("T", " ")} UTC`;
|
|
178
|
+
const short = (hash) => hash === null ? html `—` : html `<code title="${hash}">${hash.slice(0, 12)}</code>`;
|
|
179
179
|
const usd = (value) => value === null ? "—" : `$${value.toFixed(value < 1 ? 4 : 2)}`;
|
|
180
180
|
/** Only a web address links; anything else (a stored value that isn't one) is shown as text. */
|
|
181
|
-
const link = (url) => /^https:\/\/[^\s"'<>]+$/.test(url) ? `<a href="${
|
|
182
|
-
const filer = (who) => who === null || who.name === null ?
|
|
181
|
+
const link = (url) => /^https:\/\/[^\s"'<>]+$/.test(url) ? html `<a href="${url}">${url}</a>` : html `${url}`;
|
|
182
|
+
const filer = (who) => who === null || who.name === null ? html `automation` : who.kind === "person" ? html `${who.name}` : html `${who.name} (${who.kind})`;
|
|
183
183
|
export const EVIDENCE_PACK_CSS = `.evidence-pack{max-width:960px;min-width:0}.evidence-pack h1{margin:0}.evidence-pack h2{font-size:.9375rem;margin:24px 0 8px}.evidence-pack h3{font-size:.875rem;margin:16px 0 6px}` +
|
|
184
184
|
`.evidence-head{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;align-items:baseline}.evidence-pack dl{display:grid;grid-template-columns:minmax(120px,180px) minmax(0,1fr);gap:4px 16px;margin:0}` +
|
|
185
185
|
`.evidence-pack dt{color:var(--muted-foreground);font-size:.8125rem}.evidence-pack dd{margin:0;min-width:0;overflow-wrap:anywhere}.evidence-pack table{width:100%;border-collapse:collapse;font-size:.8125rem}` +
|
|
@@ -193,66 +193,62 @@ export const EVIDENCE_PACK_CSS = `.evidence-pack{max-width:960px;min-width:0}.ev
|
|
|
193
193
|
export function evidencePackHtml(pack) {
|
|
194
194
|
const task = pack.task;
|
|
195
195
|
const chain = pack.ledger.chain.ok
|
|
196
|
-
? `<p class="evidence-chain ok" data-evidence-chain="ok"><strong>Ledger chain verified</strong> · ${pack.ledger.chain.entries} entries · head ${short(pack.ledger.chain.head)}${pack.ledger.chain.checkedAt === null ? "" : ` · checked in full ${when(pack.ledger.chain.checkedAt)}`}${pack.ledger.checkpoint === null ? "" : ` · last checkpoint at entry #${pack.ledger.checkpoint.through}`}</p>`
|
|
197
|
-
: `<p class="evidence-chain problem" role="alert" data-evidence-chain="broken"><strong>Ledger chain broken</strong> · ${
|
|
196
|
+
? html `<p class="evidence-chain ok" data-evidence-chain="ok"><strong>Ledger chain verified</strong> · ${pack.ledger.chain.entries} entries · head ${short(pack.ledger.chain.head)}${pack.ledger.chain.checkedAt === null ? "" : html ` · checked in full ${when(pack.ledger.chain.checkedAt)}`}${pack.ledger.checkpoint === null ? "" : html ` · last checkpoint at entry #${pack.ledger.checkpoint.through}`}</p>`
|
|
197
|
+
: html `<p class="evidence-chain problem" role="alert" data-evidence-chain="broken"><strong>Ledger chain broken</strong> · ${pack.ledger.chain.problem}</p>`;
|
|
198
198
|
const versions = pack.versions.map((version, index) => {
|
|
199
199
|
const scope = version.scope;
|
|
200
200
|
const approved = scope?.approved ?? null;
|
|
201
201
|
const approvals = scope === null ? [] : scope.approvals.filter(one => one.digest === (approved?.digest ?? scope.digest));
|
|
202
202
|
const rows = [
|
|
203
|
-
["Filed", `${when(version.filedAt)} by ${filer(version.filedBy)}`],
|
|
204
|
-
...(scope === null ? [["Scope",
|
|
205
|
-
["Goal",
|
|
206
|
-
...(scope.touches.length ? [["Touches", scope.touches
|
|
207
|
-
...(scope.acceptance.length ? [["Acceptance", `<ol class="criteria">${scope.acceptance.map(one => `<li>${
|
|
208
|
-
...(scope.writtenBy.length ? [["Scope written by", [...new Set(scope.writtenBy.map(one => one.author))]
|
|
209
|
-
["Approved", approved === null ?
|
|
210
|
-
...(approvals.length > 1 ? [["Approvals", approvals.map(one => `${
|
|
203
|
+
["Filed", html `${when(version.filedAt)} by ${filer(version.filedBy)}`],
|
|
204
|
+
...(scope === null ? [["Scope", html `None written`]] : [
|
|
205
|
+
["Goal", html `${scope.goal}`],
|
|
206
|
+
...(scope.touches.length ? [["Touches", joinHtml(scope.touches, ", ")]] : []),
|
|
207
|
+
...(scope.acceptance.length ? [["Acceptance", html `<ol class="criteria">${scope.acceptance.map(one => html `<li>${one.statement}</li>`)}</ol>`]] : []),
|
|
208
|
+
...(scope.writtenBy.length ? [["Scope written by", joinHtml([...new Set(scope.writtenBy.map(one => one.author))], ", ")]] : []),
|
|
209
|
+
["Approved", approved === null ? html `Not approved` : html `${approved.by}${approved.basis === "mode" ? " (operating mode)" : ""} · ${when(approved.at)}${approved.current ? "" : " · the scope changed after this"}`],
|
|
210
|
+
...(approvals.length > 1 ? [["Approvals", joinHtml(approvals.map(one => html `${one.approver} · ${when(one.at)}`), html `<br>`)]] : []),
|
|
211
211
|
["Scope digest", short(scope.digest)],
|
|
212
212
|
]),
|
|
213
213
|
];
|
|
214
|
-
const runs = version.runs.length === 0 ? `<p class="meta">No agent ran.</p>` :
|
|
215
|
-
`<div class="evidence-table"><table><thead><tr><th>Run</th><th>Role</th><th>Agent</th><th>Outcome</th><th>Cost</th><th>Finished</th><th>Commit</th></tr></thead><tbody>`
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
(run.changedFiles === null ? `<p class="meta">Its changed files couldn't be read.</p>` : run.changedFiles.length === 0 ? `<p class="meta">No files changed.</p>` : `<ul class="files">${run.changedFiles.map(file => `<li>${e(file)}</li>`).join("")}</ul>`)).join("");
|
|
220
|
-
return `<section data-evidence-version="${e(version.id)}"><h2>${pack.versions.length > 1 ? `${index === 0 ? "Request" : `Revision ${index}`} · ` : "Request and approval · "}${e(version.id)}</h2><dl>${rows.map(([label, value]) => `<dt>${label}</dt><dd>${value}</dd>`).join("")}</dl>` +
|
|
221
|
-
`<h3>Agents</h3>${runs}${changes === "" ? "" : `<h3>Changes</h3>${changes}`}</section>`;
|
|
222
|
-
}).join("");
|
|
214
|
+
const runs = version.runs.length === 0 ? html `<p class="meta">No agent ran.</p>` :
|
|
215
|
+
html `<div class="evidence-table"><table><thead><tr><th>Run</th><th>Role</th><th>Agent</th><th>Outcome</th><th>Cost</th><th>Finished</th><th>Commit</th></tr></thead><tbody>${version.runs.map(run => html `<tr><td><a href="/r/${run.id}">#${run.id}</a></td><td>${run.role}</td><td>${run.agent}${run.model ? html ` · ${run.model}` : ""}</td><td>${run.outcome ?? "running"}</td><td>${usd(run.costUsd)}</td><td>${when(run.finishedAt)}</td><td>${short(run.head)}</td></tr>`)}</tbody></table></div>`;
|
|
216
|
+
const changes = version.runs.filter(run => run.role === "builder" || run.role === "repair").map(run => html `<p class="meta">Run #${run.id}${run.diff === null ? "" : html ` · diff ${short(run.diff.sha256)}${run.diff.complete ? "" : " (partial)"}`}</p>${run.changedFiles === null ? html `<p class="meta">Its changed files couldn't be read.</p>` : run.changedFiles.length === 0 ? html `<p class="meta">No files changed.</p>` : html `<ul class="files">${run.changedFiles.map(file => html `<li>${file}</li>`)}</ul>`}`);
|
|
217
|
+
return html `<section data-evidence-version="${version.id}"><h2>${pack.versions.length > 1 ? `${index === 0 ? "Request" : `Revision ${index}`} · ` : "Request and approval · "}${version.id}</h2><dl>${rows.map(([label, value]) => html `<dt>${label}</dt><dd>${value}</dd>`)}</dl><h3>Agents</h3>${runs}${changes.length === 0 ? "" : html `<h3>Changes</h3>${changes}`}</section>`;
|
|
218
|
+
});
|
|
223
219
|
const result = pack.result;
|
|
224
|
-
const outcome = result === null ? `<section><h2>Result</h2><p class="meta">No finished result yet.</p></section>` :
|
|
225
|
-
`<section data-evidence-result><h2>Result</h2><dl
|
|
226
|
-
`<dt>Checks</dt><dd>${
|
|
227
|
-
`<dt>Marked complete</dt><dd>${result.completedBy === null ? "Not yet" : `${
|
|
228
|
-
`<dt>Published</dt><dd>${result.publication === null ? "Not published" : `${
|
|
229
|
-
`<dt>Commit</dt><dd>${short(result.base)} → ${short(result.head)}</dd
|
|
230
|
-
|
|
231
|
-
`</dl></section
|
|
220
|
+
const outcome = result === null ? html `<section><h2>Result</h2><p class="meta">No finished result yet.</p></section>` :
|
|
221
|
+
joinHtml([html `<section data-evidence-result><h2>Result</h2><dl>`,
|
|
222
|
+
html `<dt>Checks</dt><dd>${result.checks.detail}${result.checks.command ? html ` <code>${result.checks.command}</code>` : ""}</dd>`,
|
|
223
|
+
html `<dt>Marked complete</dt><dd>${result.completedBy === null ? "Not yet" : html `${result.completedBy} · ${when(result.completedAt)}`}</dd>`,
|
|
224
|
+
html `<dt>Published</dt><dd>${result.publication === null ? "Not published" : html `${result.publication.state}${result.publication.prUrl ? html ` · ${link(result.publication.prUrl)}` : ""}${result.publication.remoteState ? html ` · ${result.publication.remoteState.toLowerCase()}` : ""}`}</dd>`,
|
|
225
|
+
html `<dt>Commit</dt><dd>${short(result.base)} → ${short(result.head)}</dd>`,
|
|
226
|
+
result.caveats.length ? html `<dt>Caveats</dt><dd>${joinHtml(result.caveats, html `<br>`)}</dd>` : "",
|
|
227
|
+
html `</dl></section>`]);
|
|
232
228
|
const decisions = pack.ledger.entries.filter(one => one.source !== "request");
|
|
233
229
|
const requests = pack.ledger.entries.length - decisions.length;
|
|
234
230
|
const askedWords = `${requests} ${requests === 1 ? "request" : "requests"} made through the console (accepted or refused)`;
|
|
235
|
-
const ledger = `<section><h2>Ledger</h2>${decisions.length === 0 ? `<p class="meta">No entries.</p>` :
|
|
236
|
-
`<div class="evidence-table"><table><thead><tr><th>#</th><th>Time</th><th>Who</th><th>What</th><th>Outcome</th><th>Seal</th></tr></thead><tbody>`
|
|
237
|
-
decisions.map(one => `<tr data-ledger-id="${one.id}"><td>${one.id}</td><td>${when(one.at)}</td><td>${e(one.actor)}</td><td>${e(one.action)}${one.detail ? `<br><span class="meta">${e(one.detail)}</span>` : ""}</td><td>${e(one.outcome)}</td><td>${short(one.seal?.hash ?? null)}</td></tr>`).join("") +
|
|
238
|
-
`</tbody></table></div>`}${requests > 0 ? `<p class="meta">Plus ${askedWords}, listed in the JSON.</p>` : ""}${pack.ledger.truncated ? `<p class="meta">This pack holds ${decisions.length} of the task's entries and the newest ${requests} requests; the ledger export has the rest.</p>` : ""}</section>`;
|
|
231
|
+
const ledger = html `<section><h2>Ledger</h2>${decisions.length === 0 ? html `<p class="meta">No entries.</p>` :
|
|
232
|
+
html `<div class="evidence-table"><table><thead><tr><th>#</th><th>Time</th><th>Who</th><th>What</th><th>Outcome</th><th>Seal</th></tr></thead><tbody>${decisions.map(one => html `<tr data-ledger-id="${one.id}"><td>${one.id}</td><td>${when(one.at)}</td><td>${one.actor}</td><td>${one.action}${one.detail ? html `<br><span class="meta">${one.detail}</span>` : ""}</td><td>${one.outcome}</td><td>${short(one.seal?.hash ?? null)}</td></tr>`)}</tbody></table></div>`}${requests > 0 ? html `<p class="meta">Plus ${askedWords}, listed in the JSON.</p>` : ""}${pack.ledger.truncated ? html `<p class="meta">This pack holds ${decisions.length} of the task's entries and the newest ${requests} requests; the ledger export has the rest.</p>` : ""}</section>`;
|
|
239
233
|
const cost = pack.totals.runs === 0 ? "None ran" : `${pack.totals.runs} ${pack.totals.runs === 1 ? "run" : "runs"} · ${usd(pack.totals.costUsd)}`;
|
|
240
234
|
// The rules as they stand, and each change to them (the approval above was under whichever applied then).
|
|
241
|
-
const rules = pack.rules === null ?
|
|
242
|
-
`<ul class="criteria">${pack.rules.changes.slice(-5).map(one => `<li class="meta">${when(one.at)} · ${
|
|
243
|
-
return `<article class="evidence-pack" data-evidence-pack="${
|
|
244
|
-
`<div class="evidence-head"><h1>Evidence pack</h1><p class="evidence-actions"><a href="/t/${encodeURIComponent(task.id)}/evidence?format=json" download>Download JSON</a> · <a href="/t/${encodeURIComponent(task.id)}">Back to task</a></p></div
|
|
245
|
-
`<p><strong>${
|
|
246
|
-
`<dl><dt>Task</dt><dd>${
|
|
247
|
-
`<dt>Approval rules</dt><dd>${rules}</dd><dt>Agents</dt><dd>${cost}</dd
|
|
248
|
-
`<dt>Prepared</dt><dd>${when(pack.generatedAt)} for ${
|
|
249
|
-
versions
|
|
250
|
-
`<p class="evidence-foot">Pack digest <code>${
|
|
235
|
+
const rules = pack.rules === null ? html `None` : html `${pack.rules.now}${pack.rules.changes.length === 0 ? "" :
|
|
236
|
+
html `<ul class="criteria">${pack.rules.changes.slice(-5).map(one => html `<li class="meta">${when(one.at)} · ${one.by}: ${one.change}</li>`)}</ul>`}`;
|
|
237
|
+
return joinHtml([html `<article class="evidence-pack" data-evidence-pack="${task.id}">`,
|
|
238
|
+
html `<div class="evidence-head"><h1>Evidence pack</h1><p class="evidence-actions"><a href="/t/${encodeURIComponent(task.id)}/evidence?format=json" download>Download JSON</a> · <a href="/t/${encodeURIComponent(task.id)}">Back to task</a></p></div>`,
|
|
239
|
+
html `<p><strong>${task.title}</strong></p>`,
|
|
240
|
+
html `<dl><dt>Task</dt><dd>${task.id}${task.project === null ? "" : ` · ${task.project}`} · ${task.state}</dd>`,
|
|
241
|
+
html `<dt>Approval rules</dt><dd>${rules}</dd><dt>Agents</dt><dd>${cost}</dd>`,
|
|
242
|
+
html `<dt>Prepared</dt><dd>${when(pack.generatedAt)} for ${pack.generatedBy}</dd></dl>${chain}`,
|
|
243
|
+
versions, outcome, ledger,
|
|
244
|
+
html `<p class="evidence-foot">Pack digest <code>${pack.digest}</code>. Each ledger entry's seal can be rechecked from the JSON: ${pack.ledger.recipe}.</p></article>`]);
|
|
251
245
|
}
|
|
252
246
|
/** The page as a file on its own (the CLI's --html): the same facts with just enough style to print. */
|
|
253
247
|
export function standaloneEvidenceHtml(pack) {
|
|
254
|
-
|
|
255
|
-
`<style>:root{--muted-foreground:#5c5c5c;--border:#dedede;--success:#1a7f37;--font-mono:ui-monospace,SFMono-Regular,Menlo,monospace}` +
|
|
248
|
+
const css = `:root{--muted-foreground:#5c5c5c;--border:#dedede;--success:#1a7f37;--font-mono:ui-monospace,SFMono-Regular,Menlo,monospace}` +
|
|
256
249
|
`body{font:14px/1.5 system-ui,-apple-system,sans-serif;margin:32px auto;padding:0 16px;max-width:960px;color:#171717;background:#fff}.meta{color:var(--muted-foreground);font-size:.8125rem}.evidence-actions{display:none}` +
|
|
257
|
-
EVIDENCE_PACK_CSS
|
|
250
|
+
EVIDENCE_PACK_CSS;
|
|
251
|
+
// The file is the sink: its bytes.
|
|
252
|
+
return htmlString(html `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Evidence pack · ${pack.task.id}</title>${styleElement(css)}</head><body>${evidencePackHtml(pack)}</body></html>
|
|
253
|
+
`);
|
|
258
254
|
}
|
package/dist/export-ui.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings → Data (v105): an instance operator downloads everything Standing
|
|
3
|
+
* Orders knows as one .zip. One form: the password, typed again, and a button.
|
|
4
|
+
*/
|
|
5
|
+
import { type Html } from "./html.js";
|
|
1
6
|
export declare const EXPORT_CSS: string;
|
|
2
|
-
export declare function dataExportHtml(
|
|
7
|
+
export declare function dataExportHtml(notice: {
|
|
3
8
|
problem?: string | null;
|
|
4
|
-
}):
|
|
9
|
+
}): Html;
|
package/dist/export-ui.js
CHANGED
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
* Settings → Data (v105): an instance operator downloads everything Standing
|
|
3
3
|
* Orders knows as one .zip. One form: the password, typed again, and a button.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
import { html, postForm } from "./html.js";
|
|
6
6
|
export const EXPORT_CSS = `.data-export{max-width:720px;min-width:0}.data-export label{display:grid;gap:4px;margin:14px 0 0;font-size:.875rem}` +
|
|
7
7
|
`.data-export input[type=password]{width:100%;max-width:360px;box-sizing:border-box}.data-export button[type=submit]{margin-top:14px;min-height:44px;white-space:nowrap}`;
|
|
8
|
-
export function dataExportHtml(
|
|
9
|
-
const note = notice.problem ? `<p class="problem" role="alert">${
|
|
10
|
-
return `<section class="data-export">${note}
|
|
11
|
-
`<p>Everything Toolroll knows, as one .zip: projects, tasks, runs and cost, the action ledger, evidence packs, chats, flows, teammates and settings. ` +
|
|
12
|
-
`Passwords, keys and tokens are never included.</p>` +
|
|
13
|
-
`<form method="post" action="/settings/data"><input type="hidden" name="csrf" value="${e(csrf)}">` +
|
|
14
|
-
`<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>` +
|
|
15
|
-
`<button type="submit">Download export</button></form>` +
|
|
16
|
-
`<p class="meta">The same export from the command line: <code>toolroll export --out <path> [--zip]</code></p></section>`;
|
|
8
|
+
export function dataExportHtml(notice) {
|
|
9
|
+
const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : "";
|
|
10
|
+
return html `<section class="data-export">${note}<p>Everything Toolroll knows, as one .zip: projects, tasks, runs and cost, the action ledger, evidence packs, chats, flows, subagents and settings. Passwords, keys and tokens are never included.</p>${postForm("/settings/data", html `<label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label><button type="submit">Download export</button>`)}<p class="meta">The same export from the command line: <code>toolroll export --out <path> [--zip]</code></p></section>`;
|
|
17
11
|
}
|
package/dist/export.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Full export (v105): everything Toolroll knows, for an instance
|
|
3
3
|
* operator to take away — every table of the database as JSON Lines, grouped
|
|
4
4
|
* by what it's about (projects, tasks, runs, the ledger, chats, flows,
|
|
5
|
-
*
|
|
5
|
+
* subagents, people, settings), an evidence pack per task, the settings kept
|
|
6
6
|
* in files beside the database, a README explaining the layout, and a
|
|
7
7
|
* manifest with each file's SHA-256. One folder, or the same files in a .zip.
|
|
8
8
|
*
|
|
@@ -35,14 +35,14 @@ const DERIVED_TABLE = /^(sqlite_|memory_search)/;
|
|
|
35
35
|
const SECRET_COLUMNS = new Set([
|
|
36
36
|
"api_token.secret_hash", "lead_credential.secret_hash", "approver.credential_hash", "coordinator_credential.credential_hash", "runner.credential_hash", "invite.token_hash",
|
|
37
37
|
"flow_trigger.hook_hash", "push_subscription.endpoint", "push_subscription.p256dh", "push_subscription.auth",
|
|
38
|
-
"chat_turn.credential_key", "
|
|
38
|
+
"chat_turn.credential_key", "lead_session.credential_key", "mate_turn.credential_key", "chat_runtime.push_url", "workflow_preview.token",
|
|
39
39
|
"quota.credential_fp", "oauth_grant.renew_hash", "oauth_client.source_hash",
|
|
40
40
|
]);
|
|
41
41
|
/** A column named like this holds a secret unless it is reviewed below; an unreviewed one is dropped. */
|
|
42
42
|
const SECRET_NAME = /hash|secret|token|passw|cookie|csrf|credential|p256dh|^auth$|key|nonce|endpoint|url/i;
|
|
43
43
|
/** Reviewed: named like a secret, but it isn't one (a seal, a digest, a lookup name, an address with no key). */
|
|
44
44
|
const REVIEWED_COLUMNS = new Set([
|
|
45
|
-
"artifact.key", "budget.scope_key", "
|
|
45
|
+
"artifact.key", "budget.scope_key", "lead_thread.scope_key", "diff_comment.source_key", "chat_meta.key",
|
|
46
46
|
"flow_trigger_event.key", "installation_fact.key", "mcp_idempotency.key", "memory_gap.key", "mutation.idempotency_key", "notification.dedupe_key",
|
|
47
47
|
"service_cursor.key", "ledger_checkpoint.hash", "ledger_seal.hash", "merge_intent.grant_terms_hash", "plan_revision.parent_hash",
|
|
48
48
|
"publication.body_hash", "publication.pr_url", "team_message.payload_hash", "team_request.payload_hash", "backend_grant.credential_scope",
|
|
@@ -53,9 +53,9 @@ const USAGE_COLUMN = /(^|_)tokens(_|$)|output_tokens/;
|
|
|
53
53
|
export function categoryOf(table) {
|
|
54
54
|
const rules = [
|
|
55
55
|
[/^(action_ledger|ledger_seal|ledger_checkpoint|sync_ledger)$/, "ledger"],
|
|
56
|
-
[/^
|
|
56
|
+
[/^subagent/, "subagents"],
|
|
57
57
|
[/^(flow|workflow_)/, "flows"],
|
|
58
|
-
[/^(chat_|
|
|
58
|
+
[/^(chat_|lead_|team_|notification|push_)/, "chats"],
|
|
59
59
|
[/^(approver|api_token|lead_credential|invite|sso_identity|coordinator_|oauth_)/, "people"],
|
|
60
60
|
[/^(run|artifact|claim|worktree$|execution_slot|criterion_review|diff_comment|proof_|review_request|repair_chain|incident|publication$|merge_|side_spend|knowledge_snapshot|learning_snapshot|skill_snapshot)/, "runs"],
|
|
61
61
|
[/^(task|hold$|plan_|scope_|tool_seal|decision|external_|skill_test)/, "tasks"],
|
|
@@ -214,9 +214,9 @@ Everything this installation knew on ${now.toISOString().slice(0, 16).replace("T
|
|
|
214
214
|
- \`runs/\`: agent runs with usage and cost (\`run\`, \`run_spend\`), their records of artifacts, proofs, reviews and publications.
|
|
215
215
|
- \`ledger/\`: the action ledger (\`action_ledger\`), each entry's seal (\`ledger_seal\`) and the checkpoints (\`ledger_checkpoint\`).
|
|
216
216
|
- \`evidence-packs/\`: one JSON file per task: who asked, the exact terms and who approved them, runs and cost, checks, completion and its ledger entries.
|
|
217
|
-
- \`chats/\`: conversations with the lead and
|
|
217
|
+
- \`chats/\`: conversations with the lead and subagents, and Telegram, Slack, Discord and Teams messages and notifications.
|
|
218
218
|
- \`flows/\`: flows, their cards, scripts and triggers (schedules included) and saved recipes.
|
|
219
|
-
- \`
|
|
219
|
+
- \`subagents/\`: subagents, their versions, memory, tools and turns.
|
|
220
220
|
- \`people/\`: accounts, API token names and invitations (no passwords or tokens).
|
|
221
221
|
- \`settings/\`: models, budgets, spend and permission defaults, provider readings; \`files.json\` holds the settings kept in files.
|
|
222
222
|
- \`other/\`: everything else the database keeps (workers, leases, queues).
|
package/dist/flow-engine.d.ts
CHANGED
|
@@ -72,8 +72,8 @@ export declare function decideFlowCard(store: Store, input: {
|
|
|
72
72
|
draft?: string | null;
|
|
73
73
|
/** The visit the person saw (a chat button's): a card that moved on since is refused. */
|
|
74
74
|
entry?: number;
|
|
75
|
-
/** v92: the
|
|
76
|
-
|
|
75
|
+
/** v92: the subagent (by handle) deciding a zone it staffs; `actor` is then how it reads in history. */
|
|
76
|
+
subagent?: string;
|
|
77
77
|
/** Where the decision was made, for its ledger line: "the console", "the command line", "Telegram". */
|
|
78
78
|
where?: string;
|
|
79
79
|
}, now: Date): FlowDecision;
|
package/dist/flow-engine.js
CHANGED
|
@@ -18,7 +18,7 @@ import { readVerifiedReport, scanForSecrets } from "./evidence.js";
|
|
|
18
18
|
import { cardFollowers, notifyPeople } from "./flow-people.js";
|
|
19
19
|
import { DRAFT_CHARS, keptDraft } from "./flow-draft.js";
|
|
20
20
|
import { chooseStep, flowPersonOf, sendStep } from "./flow-send.js";
|
|
21
|
-
import { parseSoul,
|
|
21
|
+
import { parseSoul, subagentLabel } from "./subagents.js";
|
|
22
22
|
import { withStageHandoff } from "./contracts/stage-output.js";
|
|
23
23
|
/** The flow's drawing, or null when it can't be read (the card then says so and waits). */
|
|
24
24
|
export function flowDefinitionOf(flow) {
|
|
@@ -42,15 +42,15 @@ function fill(template, card) {
|
|
|
42
42
|
}
|
|
43
43
|
const titleIn = (definition, id) => definition.stages.find(one => one.id === id)?.title ?? id;
|
|
44
44
|
const flowDefinitionStage = (definition, id) => definition.stages.find(one => one.id === id);
|
|
45
|
-
/** A
|
|
46
|
-
function
|
|
45
|
+
/** A subagent's name as its soul file gives it (its handle when the file can't be read). */
|
|
46
|
+
function subagentName(mate) {
|
|
47
47
|
const read = parseSoul(mate.soul);
|
|
48
48
|
return read.ok ? read.soul.name : mate.handle;
|
|
49
49
|
}
|
|
50
|
-
/** What a
|
|
50
|
+
/** What a subagent said when it handed a card to its person, or why it couldn't decide. */
|
|
51
51
|
function handoffWords(mate, turn) {
|
|
52
52
|
const read = parseSoul(mate.soul);
|
|
53
|
-
const label = read.ok ?
|
|
53
|
+
const label = read.ok ? subagentLabel(read.soul) : mate.handle;
|
|
54
54
|
let note = "";
|
|
55
55
|
try {
|
|
56
56
|
const decided = JSON.parse(turn.decisionJson ?? "{}");
|
|
@@ -187,20 +187,20 @@ function advanceCard(store, flow, definition, card, now, options, outcome) {
|
|
|
187
187
|
case "approval": {
|
|
188
188
|
const decider = deciderOf(stage, flow);
|
|
189
189
|
const who = decider ?? "an approver";
|
|
190
|
-
// v92: a zone an active
|
|
191
|
-
// about the card only when the
|
|
192
|
-
// v102: on a protected project
|
|
193
|
-
const mate = stage.
|
|
190
|
+
// v92: a zone an active subagent staffs is its to decide first (the step pass runs it). A person hears
|
|
191
|
+
// about the card only when the subagent hands it over, can't decide, or is paused or gone.
|
|
192
|
+
// v102: on a protected project a subagent never decides; the person does.
|
|
193
|
+
const mate = stage.subagent === undefined || store.approvalRules(flow.repo).protectProject ? null : store.subagentByHandle(flow.repo, stage.subagent);
|
|
194
194
|
const turn = mate === null ? null : store.flowStepRun(card.id, card.entry);
|
|
195
|
-
const handed = turn !== null && turn.kind === "
|
|
195
|
+
const handed = turn !== null && turn.kind === "subagent" && (turn.state === "failed" || turn.state === "passed");
|
|
196
196
|
if (mate !== null && mate.state === "active" && !handed) {
|
|
197
|
-
const name =
|
|
197
|
+
const name = subagentName(mate);
|
|
198
198
|
if (card.waiting !== `${name} is deciding`)
|
|
199
199
|
store.updateFlowCard(card.id, { waiting: `${name} is deciding` }, now);
|
|
200
200
|
return;
|
|
201
201
|
}
|
|
202
202
|
const handoff = handed && mate !== null ? handoffWords(mate, turn) : null;
|
|
203
|
-
if (card.waiting === null || (mate !== null && card.waiting === `${
|
|
203
|
+
if (card.waiting === null || (mate !== null && card.waiting === `${subagentName(mate)} is deciding`)) {
|
|
204
204
|
// A named decider hears it alone; "anyone who approves" pages everyone who can.
|
|
205
205
|
// With a draft in front of it, the decision carries the draft itself: it can be read (and answered) where it arrives.
|
|
206
206
|
const draft = draftFor(definition, stage);
|
|
@@ -447,8 +447,8 @@ export function cancelFlowCard(store, card, actor, now) {
|
|
|
447
447
|
/** The draft a decision is about: the draft zone that sends its cards to this one. */
|
|
448
448
|
export function draftFor(definition, stage) {
|
|
449
449
|
return definition.stages.find(one => one.kind === "draft" && one.next === stage.id)
|
|
450
|
-
// A
|
|
451
|
-
?? definition.stages.find(one => one.kind === "
|
|
450
|
+
// A subagent's zone that leads here (by its next, or one of its answers) wrote the draft this decision checks (starter kits).
|
|
451
|
+
?? definition.stages.find(one => one.kind === "subagent" && (one.next === stage.id || (one.routes ?? []).some(route => route.to === stage.id))) ?? null;
|
|
452
452
|
}
|
|
453
453
|
/** Back to a build zone with a finished result: a revision of that same work, carrying the note; null when it isn't one. */
|
|
454
454
|
export function revisionWithNote(store, card, target, note, actor, repos, evidenceRoot, now) {
|
|
@@ -478,21 +478,21 @@ export function decideFlowCard(store, input, now) {
|
|
|
478
478
|
const stage = definition.stages.find(one => one.id === card.stage);
|
|
479
479
|
if (stage === undefined || stage.kind !== "approval" || (input.entry !== undefined && input.entry !== card.entry))
|
|
480
480
|
return { ok: false, message: "That card has moved on since; nothing was changed." };
|
|
481
|
-
// A
|
|
481
|
+
// A subagent decides only a zone it staffs; its person (and only them) can always decide instead.
|
|
482
482
|
const decider = deciderOf(stage, flow);
|
|
483
|
-
if (input.
|
|
483
|
+
if (input.subagent !== undefined ? stage.subagent !== input.subagent : decider !== null && decider !== input.actor)
|
|
484
484
|
return { ok: false, message: `Only ${decider ?? "an approver"} decides here.` };
|
|
485
|
-
// v102: a protected project's decisions are a person's, never
|
|
486
|
-
if (input.
|
|
485
|
+
// v102: a protected project's decisions are a person's, never a subagent's.
|
|
486
|
+
if (input.subagent !== undefined && store.approvalRules(flow.repo).protectProject)
|
|
487
487
|
return { ok: false, message: "This project is protected: a person decides here." };
|
|
488
488
|
// Every decision is one ledger line, the same wherever it was made; only `via` says where.
|
|
489
489
|
const ledger = (outcome) => store.recordAction({ at: now.toISOString(), actor: input.actor, repo: flow.repo, taskId: card.primaryTask, runId: null,
|
|
490
|
-
action: "flow decision", outcome, source: "request", detail: `${flow.name} · card ${card.id} · ${stage.title}${input.
|
|
490
|
+
action: "flow decision", outcome, source: "request", detail: `${flow.name} · card ${card.id} · ${stage.title}${input.subagent === undefined ? "" : ` · subagent ${input.subagent}`} · via ${input.where ?? "Toolroll"}` });
|
|
491
491
|
if (input.decision === "approve") {
|
|
492
492
|
// An edited draft replaces the one Claude wrote, so the steps after this send what the person approved.
|
|
493
493
|
const draft = draftFor(definition, stage);
|
|
494
|
-
// A person's edit over the draft limit is refused with it, never cut. (A
|
|
495
|
-
if (draft !== null && input.
|
|
494
|
+
// A person's edit over the draft limit is refused with it, never cut. (A subagent's was asked once to shorten and is kept whole.)
|
|
495
|
+
if (draft !== null && input.subagent === undefined && typeof input.draft === "string" && input.draft.trim().length > DRAFT_CHARS) {
|
|
496
496
|
return { ok: false, message: `Keep the draft to ${DRAFT_CHARS.toLocaleString("en-US")} characters; this is ${input.draft.trim().length.toLocaleString("en-US")}.` };
|
|
497
497
|
}
|
|
498
498
|
if (draft !== null && typeof input.draft === "string" && input.draft.trim() !== "" && input.draft.trim() !== card.outputs[draft.id]?.trim()) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type GalleryAnswers, type GalleryPreview, type GalleryTemplate, type GalleryTool } from "./flow-gallery.js";
|
|
2
2
|
import { type FlowDefinition, type FlowStage } from "./flows.js";
|
|
3
|
+
import { type Html } from "./html.js";
|
|
3
4
|
export declare const GALLERY_CSS: string;
|
|
4
5
|
/** A flow's main path, as a person reads it: from the start, each step's first way on (a sort's first answer, a choose's
|
|
5
6
|
* first option that goes somewhere, else next; a check's failure when passing just ends), never back to a step shown. */
|
|
6
7
|
export declare function mainPath(definition: FlowDefinition): FlowStage[];
|
|
7
8
|
/** A flow's steps in order, joined by arrows; a person's decisions carry the accent. */
|
|
8
|
-
export declare function stepStrip(definition: FlowDefinition):
|
|
9
|
+
export declare function stepStrip(definition: FlowDefinition): Html;
|
|
9
10
|
/** The gallery: every template, grouped, each with one "Use this". `repo` carries a chosen project to the template's page. */
|
|
10
11
|
export declare function galleryHtml(input: {
|
|
11
12
|
repo: string | null;
|
|
@@ -14,7 +15,7 @@ export declare function galleryHtml(input: {
|
|
|
14
15
|
id: string;
|
|
15
16
|
state: "connected" | "open" | "taken";
|
|
16
17
|
}[] | null;
|
|
17
|
-
}):
|
|
18
|
+
}): Html;
|
|
18
19
|
/** A template's page: its questions, then what it will do and never do, then Create. */
|
|
19
20
|
export declare function galleryUseHtml(input: {
|
|
20
21
|
template: GalleryTemplate;
|
|
@@ -32,4 +33,4 @@ export declare function galleryUseHtml(input: {
|
|
|
32
33
|
/** The template's tools in this project; `said` is how the last Connect went. */
|
|
33
34
|
tools?: readonly GalleryTool[];
|
|
34
35
|
said?: string | null;
|
|
35
|
-
}):
|
|
36
|
+
}): Html;
|
package/dist/flow-gallery-ui.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { brandMarkHtml } from "./brand-mark.js";
|
|
8
8
|
import { BLANK, GALLERY, GALLERY_GROUPS, galleryDiagram, galleryToolsOf, OUTDATED_COMMANDS, SEND_RESULT, sendsAlready } from "./flow-gallery.js";
|
|
9
9
|
import { choiceTargets } from "./flows.js";
|
|
10
|
-
|
|
10
|
+
import { html, joinHtml, postForm } from "./html.js";
|
|
11
11
|
export const GALLERY_CSS = `.gallery{max-width:1120px;min-width:0}.gallery h2{margin:28px 0 10px;font-size:.9375rem}.gallery h2:first-of-type{margin-top:8px}` +
|
|
12
12
|
`.gallery-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px}` +
|
|
13
13
|
`.gallery-card{display:flex;flex-direction:column;gap:8px;padding:16px;border:1px solid var(--so-line);border-radius:10px;background:var(--so-paper);min-width:0}` +
|
|
@@ -53,30 +53,28 @@ export function mainPath(definition) {
|
|
|
53
53
|
const stepName = (stage) => stage.kind === "choose" ? "You choose" : stage.kind === "approval" ? "You approve" : stage.kind === "send" ? "Sent to you" : stage.title;
|
|
54
54
|
/** A flow's steps in order, joined by arrows; a person's decisions carry the accent. */
|
|
55
55
|
export function stepStrip(definition) {
|
|
56
|
-
return `<ol class="gallery-steps" aria-label="Steps">${mainPath(definition).map((one, i) => {
|
|
57
|
-
const name =
|
|
58
|
-
return `<li>${i === 0 ? "" : `<span aria-hidden="true">→</span>`}${one.kind === "choose" || one.kind === "approval" ? `<b data-person>${name}</b>` : name}</li>`;
|
|
59
|
-
})
|
|
56
|
+
return html `<ol class="gallery-steps" aria-label="Steps">${mainPath(definition).map((one, i) => {
|
|
57
|
+
const name = stepName(one);
|
|
58
|
+
return html `<li>${i === 0 ? "" : html `<span aria-hidden="true">→</span>`}${one.kind === "choose" || one.kind === "approval" ? html `<b data-person>${name}</b>` : name}</li>`;
|
|
59
|
+
})}</ol>`;
|
|
60
60
|
}
|
|
61
|
-
const needsList = (template) => template.needs.length === 0 ? (template.tools ?? []).length > 0 ? "" : `<ul class="gallery-needs"><li>Nothing to connect</li></ul>`
|
|
62
|
-
: `<ul class="gallery-needs" aria-label="Needs">${template.needs.map(one => `<li>${
|
|
61
|
+
const needsList = (template) => template.needs.length === 0 ? (template.tools ?? []).length > 0 ? "" : html `<ul class="gallery-needs"><li>Nothing to connect</li></ul>`
|
|
62
|
+
: html `<ul class="gallery-needs" aria-label="Needs">${template.needs.map(one => html `<li>${one}</li>`)}</ul>`;
|
|
63
63
|
/** A tool's state in words, as Settings → Integrations badges it; nothing without a project. */
|
|
64
64
|
const STATE = { connected: ["connected", "Connected"], open: ["not-set-up", "Not connected"], taken: ["not-set-up", "Added another way"] };
|
|
65
|
-
const toolState = (tool) => tool.state === null ? "" : `<span class="integration-state integration-state--${STATE[tool.state][0]}"><i aria-hidden="true"></i>${STATE[tool.state][1]}</span>`;
|
|
65
|
+
const toolState = (tool) => tool.state === null ? "" : html `<span class="integration-state integration-state--${STATE[tool.state][0]}"><i aria-hidden="true"></i>${STATE[tool.state][1]}</span>`;
|
|
66
66
|
const toolMark = (tool) => brandMarkHtml(tool.id.replace(/-desktop$/, ""), tool.label, tool.state === "connected");
|
|
67
67
|
const toolsList = (tools) => tools.length === 0 ? ""
|
|
68
|
-
: `<ul class="gallery-tools" aria-label="Tools">${tools.map(one => `<li data-tool="${
|
|
68
|
+
: html `<ul class="gallery-tools" aria-label="Tools">${tools.map(one => html `<li data-tool="${one.id}" data-state="${one.state ?? "unknown"}">${toolMark(one)}<strong>${one.label}</strong>${toolState(one)}</li>`)}</ul>`;
|
|
69
69
|
/** The gallery: every template, grouped, each with one "Use this". `repo` carries a chosen project to the template's page. */
|
|
70
70
|
export function galleryHtml(input) {
|
|
71
71
|
const href = (id) => `/flows/new/${id}${input.repo === null ? "" : `?repo=${encodeURIComponent(input.repo)}`}`;
|
|
72
72
|
const card = (template) => {
|
|
73
73
|
const definition = galleryDiagram(template);
|
|
74
|
-
return `<article class="gallery-card" data-template="${
|
|
75
|
-
stepStrip(definition) + toolsList(galleryToolsOf(template, definition, input.repo === null ? null : input.connections ?? null)) + needsList(template) +
|
|
76
|
-
(input.canUse ? `<a class="button-link" href="${e(href(template.id))}" aria-label="Use ${e(template.name)}">Use this</a>` : "") + `</article>`;
|
|
74
|
+
return html `<article class="gallery-card" data-template="${template.id}"><h3>${template.name}</h3><p class="gallery-promise">${template.promise}</p>${stepStrip(definition)}${toolsList(galleryToolsOf(template, definition, input.repo === null ? null : input.connections ?? null))}${needsList(template)}${input.canUse && html `<a class="button-link" href="${href(template.id)}" aria-label="Use ${template.name}">Use this</a>`}</article>`;
|
|
77
75
|
};
|
|
78
|
-
const groups = GALLERY_GROUPS.map(group => `<h2 id="gallery-${group.id}">${
|
|
79
|
-
return `<section class="gallery">${groups}${input.canUse ? `<p class="gallery-blank meta"><a href="${
|
|
76
|
+
const groups = GALLERY_GROUPS.map(group => html `<h2 id="gallery-${group.id}">${group.label}</h2><div class="gallery-grid" data-group="${group.id}">${GALLERY.filter(one => one.group === group.id).map(card)}</div>`);
|
|
77
|
+
return html `<section class="gallery">${groups}${input.canUse ? html `<p class="gallery-blank meta"><a href="${href(BLANK.id)}">Start from a blank flow</a></p>` : html `<p class="meta">An approver creates flows.</p>`}</section>`;
|
|
80
78
|
}
|
|
81
79
|
/** A template's page: its questions, then what it will do and never do, then Create. */
|
|
82
80
|
export function galleryUseHtml(input) {
|
|
@@ -86,43 +84,30 @@ export function galleryUseHtml(input) {
|
|
|
86
84
|
const missing = tools.filter(one => one.state !== "connected" && one.state !== "taken");
|
|
87
85
|
// Each tool with its state; one the project lacks offers its own Connect (the Tools page's, with the same password), and comes back here.
|
|
88
86
|
const connect = (tool) => {
|
|
89
|
-
const head = `<p class="gallery-tool-head">${toolMark(tool)} <strong>${
|
|
87
|
+
const head = html `<p class="gallery-tool-head">${toolMark(tool)} <strong>${tool.label}</strong> ${toolState(tool)}</p>`;
|
|
90
88
|
// Added another way, it is there: nothing to connect.
|
|
91
89
|
if (tool.state === "connected" || tool.state === "taken" || input.csrf === "")
|
|
92
|
-
return `<div class="gallery-connect" data-tool="${
|
|
90
|
+
return html `<div class="gallery-connect" data-tool="${tool.id}" data-state="${String(tool.state)}">${head}</div>`;
|
|
93
91
|
const how = tool.id === "figma-desktop" ? "Open the Figma desktop app and turn on its Dev Mode MCP server in Preferences." : `You sign in on ${tool.label}, then come back here.`;
|
|
94
|
-
return `<div class="gallery-connect" data-tool="${
|
|
95
|
-
`<input type="hidden" name="csrf" value="${e(input.csrf)}"><input type="hidden" name="repo" value="${e(input.repo)}"><input type="hidden" name="shown" value="${e(input.repo)}"><input type="hidden" name="template" value="${e(template.id)}">` +
|
|
96
|
-
`<p class="meta">${e(how)}</p><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label>` +
|
|
97
|
-
`<button name="service" value="${e(tool.id)}">Connect ${e(tool.label)}</button></form></div>`;
|
|
92
|
+
return html `<div class="gallery-connect" data-tool="${tool.id}" data-state="open">${head}${postForm("/settings/tools/connect", html `<p class="meta">${how}</p><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password" required></label><button name="service" value="${tool.id}">Connect ${tool.label}</button>`, { hidden: { repo: input.repo, shown: input.repo, template: template.id } })}</div>`;
|
|
98
93
|
};
|
|
99
|
-
const toolsBlock = tools.length === 0 ? "" : `<section class="gallery-tools-use" aria-label="Tools">${tools.map(connect)
|
|
94
|
+
const toolsBlock = tools.length === 0 ? "" : html `<section class="gallery-tools-use" aria-label="Tools">${tools.map(connect)}</section>`;
|
|
100
95
|
// Made without a tool, the preview names the zone that needs it.
|
|
101
|
-
const zones = (tool) => tool.zones.map(zone => `“${
|
|
102
|
-
const needing = missing.filter(one => one.zones.length > 0).map(one => `<p class="meta" data-needs-tool="${
|
|
103
|
-
tools.filter(one => one.state === "taken" && one.zones.length > 0).map(one => `<p class="meta" data-tool-taken="${e(one.id)}">${zones(one)} use${one.zones.length === 1 ? "s" : ""} ${e(one.label)}, added another way.</p>`).join("");
|
|
96
|
+
const zones = (tool) => joinHtml(tool.zones.map(zone => html `“${zone}”`), " and ");
|
|
97
|
+
const needing = html `${missing.filter(one => one.zones.length > 0).map(one => html `<p class="meta" data-needs-tool="${one.id}">${zones(one)} need${one.zones.length === 1 ? "s" : ""} ${one.label}, which isn't connected.</p>`)}${tools.filter(one => one.state === "taken" && one.zones.length > 0).map(one => html `<p class="meta" data-tool-taken="${one.id}">${zones(one)} use${one.zones.length === 1 ? "s" : ""} ${one.label}, added another way.</p>`)}`;
|
|
104
98
|
const field = (ask) => {
|
|
105
99
|
const value = input.answers[ask.key] ?? ask.default;
|
|
106
|
-
const hint = ask.hint === undefined ? "" : ` <small>${
|
|
100
|
+
const hint = ask.hint === undefined ? "" : html ` <small>${ask.hint}</small>`;
|
|
107
101
|
if (ask.key === "outdated")
|
|
108
|
-
return `<label>${
|
|
109
|
-
return `<label>${
|
|
102
|
+
return html `<label>${ask.label}<select name="outdated">${Object.entries(OUTDATED_COMMANDS).map(([id, one]) => html `<option value="${id}"${id === value && html ` selected`}>${one.label}</option>`)}</select></label>`;
|
|
103
|
+
return html `<label>${ask.label}${hint}<input name="${ask.key}" value="${value}" required maxlength="${ask.key === "command" ? 500 : ask.key === "team" ? 12 : 100}"${ask.key === "command" ? html ` spellcheck="false" autocapitalize="off"` : ask.key === "team" ? html ` spellcheck="false" autocapitalize="characters"` : ""}></label>`;
|
|
110
104
|
};
|
|
111
105
|
const project = input.projects.length > 1
|
|
112
|
-
? `<label>Project<select name="repo">${input.projects.map(one => `<option value="${
|
|
113
|
-
: `<input type="hidden" name="repo" value="${
|
|
114
|
-
const preview = input.preview === null ? "" : `<section class="gallery-preview" aria-labelledby="gallery-will">`
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return `<section class="gallery-use" data-gallery-template="${e(template.id)}">` +
|
|
120
|
-
(input.problem === null ? "" : `<p class="problem" role="alert">${e(input.problem)}</p>`) + (input.said ? `<p role="status">${e(input.said)}</p>` : "") +
|
|
121
|
-
`<p>${e(template.promise)}</p>${input.diagram === null ? "" : stepStrip(input.diagram)}${needsList(template)}${toolsBlock}` +
|
|
122
|
-
`<form method="post" action="/flows/new/${e(template.id)}" data-gallery-use><input type="hidden" name="csrf" value="${e(input.csrf)}">` +
|
|
123
|
-
`<input type="hidden" name="previewed" value="${e(input.preview?.digest ?? "")}">${project}${template.asks.map(field).join("")}` +
|
|
124
|
-
`<label>Name<input name="name" value="${e(input.name)}" maxlength="80"></label>` +
|
|
125
|
-
(sendsAlready(template) ? "" : `<label class="gallery-check"><input type="checkbox" name="${SEND_RESULT.key}" value="yes"${input.answers[SEND_RESULT.key] === "yes" ? " checked" : ""} data-send-result><span>${e(SEND_RESULT.title)}<small>When a card finishes, what was done comes to you in your chat apps.</small></span></label>`) + preview +
|
|
126
|
-
`<p class="gallery-actions">${input.preview === null ? "" : `<button type="submit" name="intent" value="create"${missing.length > 0 ? ' class="secondary" data-without-tools' : ""}>Create flow</button>`}<button type="submit" name="intent" value="preview" class="secondary">${input.preview === null ? "Preview" : "Update preview"}</button></p>` +
|
|
127
|
-
`</form><p class="meta"><a href="/flows/new">All templates</a></p></section>`;
|
|
106
|
+
? html `<label>Project<select name="repo">${input.projects.map(one => html `<option value="${one.path}"${one.path === input.repo && html ` selected`}>${one.name}</option>`)}</select></label>`
|
|
107
|
+
: html `<input type="hidden" name="repo" value="${input.repo}">`;
|
|
108
|
+
const preview = input.preview === null ? "" : html `<section class="gallery-preview" aria-labelledby="gallery-will"><h2 id="gallery-will">What it will do</h2><ul>${input.preview.built.does.map(one => html `<li>${one}</li>`)}</ul><p class="gallery-never">${input.preview.built.never}</p>${needing}${input.preview.startsFrom.length === 0 ? html `<p class="meta">Cards start when you add them.</p>` : input.preview.startsFrom.map(one => html `<p class="meta">Starts from: ${one}</p>`)}<details><summary>Every step</summary><ol>${input.preview.steps.map(one => html `<li>${one.replace(/^\d+\.\s/, "")}</li>`)}</ol></details></section>`;
|
|
109
|
+
const sendResult = !sendsAlready(template) && html `<label class="gallery-check"><input type="checkbox" name="${SEND_RESULT.key}" value="yes"${input.answers[SEND_RESULT.key] === "yes" && html ` checked`} data-send-result><span>${SEND_RESULT.title}<small>When a card finishes, what was done comes to you in your chat apps.</small></span></label>`;
|
|
110
|
+
const actions = html `<p class="gallery-actions">${input.preview !== null && html `<button type="submit" name="intent" value="create"${missing.length > 0 && html ` class="secondary" data-without-tools`}>Create flow</button>`}<button type="submit" name="intent" value="preview" class="secondary">${input.preview === null ? "Preview" : "Update preview"}</button></p>`;
|
|
111
|
+
const form = postForm(`/flows/new/${template.id}`, html `${project}${template.asks.map(field)}<label>Name<input name="name" value="${input.name}" maxlength="80"></label>${sendResult}${preview}${actions}`, { attrs: { "data-gallery-use": true }, hidden: { previewed: input.preview?.digest ?? "" } });
|
|
112
|
+
return html `<section class="gallery-use" data-gallery-template="${template.id}">${input.problem !== null && html `<p class="problem" role="alert">${input.problem}</p>`}${input.said ? html `<p role="status">${input.said}</p>` : ""}<p>${template.promise}</p>${input.diagram !== null && stepStrip(input.diagram)}${needsList(template)}${toolsBlock}${form}<p class="meta"><a href="/flows/new">All templates</a></p></section>`;
|
|
128
113
|
}
|
package/dist/flow-insights.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type StepRunInsight = {
|
|
|
58
58
|
entry: number;
|
|
59
59
|
zone: string;
|
|
60
60
|
zoneTitle: string;
|
|
61
|
-
kind: "check" | "update" | "sort" | "draft" | "request" | "email" | "tool" | "
|
|
61
|
+
kind: "check" | "update" | "sort" | "draft" | "request" | "email" | "tool" | "subagent";
|
|
62
62
|
script: string | null;
|
|
63
63
|
version: number | null;
|
|
64
64
|
state: FlowStepRunRow["state"];
|