toolroll 0.9.72 → 0.9.74
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 +108 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent-fence.d.ts +5 -0
- package/dist/agent-fence.js +42 -7
- package/dist/agent-turns.js +5 -0
- package/dist/agents-live.d.ts +31 -0
- package/dist/agents-live.js +43 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +40 -0
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -30
- package/dist/browser-workspace.d.ts +6 -0
- package/dist/builder.js +1 -1
- package/dist/chat-actions.js +13 -8
- package/dist/chat-continuity.js +10 -0
- package/dist/chat-decide.d.ts +47 -1
- package/dist/chat-decide.js +170 -4
- package/dist/chat-quiet.js +3 -3
- package/dist/check-report.js +2 -1
- package/dist/coding-provider.js +4 -2
- package/dist/commit-extract.d.ts +17 -0
- package/dist/commit-extract.js +82 -4
- package/dist/contracts/console-api.d.ts +3 -3
- package/dist/contracts/console-api.js +4 -4
- package/dist/contracts/release-terms.d.ts +7 -1
- package/dist/contracts/release-terms.js +6 -2
- package/dist/deploy-gate.js +5 -2
- package/dist/desktop-update.d.ts +1 -1
- package/dist/dispose.js +35 -2
- package/dist/exec.d.ts +1 -1
- package/dist/exec.js +20 -19
- package/dist/first-run.d.ts +17 -1
- package/dist/first-run.js +38 -0
- package/dist/flow-gallery-ui.js +4 -2
- package/dist/flow-gallery.d.ts +3 -0
- package/dist/flow-gallery.js +6 -2
- package/dist/flow-triggers.d.ts +8 -0
- package/dist/flow-triggers.js +15 -0
- package/dist/flows-cli.js +13 -3
- package/dist/guides.js +10 -1
- package/dist/invoke.d.ts +13 -0
- package/dist/invoke.js +21 -12
- package/dist/kits-ui.js +4 -2
- package/dist/kits.d.ts +6 -4
- package/dist/kits.js +9 -11
- package/dist/knowledge-ui.d.ts +4 -1
- package/dist/knowledge-ui.js +10 -5
- package/dist/lead-progress.d.ts +4 -1
- package/dist/lead-progress.js +3 -1
- package/dist/lead.js +4 -0
- package/dist/live-bus.d.ts +2 -0
- package/dist/live-bus.js +1 -0
- package/dist/live-children-read.d.ts +52 -0
- package/dist/live-children-read.js +333 -0
- package/dist/live-children.d.ts +73 -0
- package/dist/live-children.js +318 -0
- package/dist/live-detail.d.ts +4 -3
- package/dist/live-detail.js +9 -5
- package/dist/live-input.js +7 -8
- package/dist/live-main-read.d.ts +19 -0
- package/dist/live-main-read.js +91 -0
- package/dist/live-open.d.ts +28 -0
- package/dist/live-open.js +125 -0
- package/dist/live-records.d.ts +10 -1
- package/dist/live-records.js +16 -5
- package/dist/live.d.ts +54 -22
- package/dist/live.js +218 -69
- package/dist/mcp-connect.d.ts +10 -0
- package/dist/mcp-connect.js +2 -0
- package/dist/operate.js +3 -2
- package/dist/peek-cli.d.ts +10 -3
- package/dist/peek-cli.js +17 -8
- package/dist/planner.js +1 -1
- package/dist/project-tools.js +3 -1
- package/dist/protected-storage.d.ts +67 -0
- package/dist/protected-storage.js +398 -0
- package/dist/provider-limits.d.ts +3 -1
- package/dist/provider-limits.js +5 -2
- package/dist/provider.js +6 -12
- package/dist/release-adapters.d.ts +8 -1
- package/dist/release-adapters.js +84 -14
- package/dist/release-authority.d.ts +6 -0
- package/dist/release-authority.js +9 -0
- package/dist/release-card.d.ts +76 -0
- package/dist/release-card.js +252 -0
- package/dist/release-carry.d.ts +3 -0
- package/dist/release-carry.js +23 -4
- package/dist/release-cli.js +36 -3
- package/dist/release-git.d.ts +43 -0
- package/dist/release-git.js +106 -0
- package/dist/release.d.ts +11 -2
- package/dist/release.js +82 -6
- package/dist/repository-context-ui.js +2 -1
- package/dist/run-efficiency.d.ts +3 -3
- package/dist/run-efficiency.js +20 -8
- package/dist/scope.d.ts +7 -0
- package/dist/scope.js +22 -5
- package/dist/scout.d.ts +3 -0
- package/dist/scout.js +5 -3
- package/dist/serve.js +34 -23
- package/dist/server/agents-live-lead.d.ts +47 -0
- package/dist/server/agents-live-lead.js +116 -0
- package/dist/server/agents-live-room.d.ts +23 -0
- package/dist/server/agents-live-room.js +88 -0
- package/dist/server/chat.js +84 -30
- package/dist/server/chrome.js +2 -2
- package/dist/server/console.css +68 -40
- package/dist/server/flows.js +40 -10
- package/dist/server/live.d.ts +4 -0
- package/dist/server/live.js +42 -2
- package/dist/server/pages.js +81 -1
- package/dist/server/render-chat.d.ts +15 -1
- package/dist/server/render-chat.js +14 -7
- package/dist/server/render-pages.d.ts +8 -4
- package/dist/server/render-pages.js +55 -50
- package/dist/server/render-settings.js +1 -1
- package/dist/server/render-tasks.js +10 -3
- package/dist/server/route-table.js +5 -1
- package/dist/server/runtime.d.ts +1 -1
- package/dist/server/settings.js +29 -8
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +59 -41
- package/dist/setup-guide.d.ts +2 -2
- package/dist/store.d.ts +34 -1
- package/dist/store.js +81 -2
- package/dist/stream-scope.d.ts +18 -0
- package/dist/stream-scope.js +28 -0
- package/dist/subagent-memory.d.ts +1 -0
- package/dist/subagent-memory.js +1 -1
- package/dist/subagent-tools.d.ts +22 -6
- package/dist/subagent-tools.js +44 -8
- package/dist/subagent-work.d.ts +2 -0
- package/dist/subagent-work.js +5 -2
- package/dist/subagents-ui.js +6 -4
- package/dist/subscription-chat.js +5 -3
- package/dist/telegram.d.ts +6 -1
- package/dist/telegram.js +58 -6
- package/package.json +1 -1
package/dist/flow-triggers.js
CHANGED
|
@@ -368,6 +368,18 @@ export function rawTakesDeliveries(raw) {
|
|
|
368
368
|
const one = raw;
|
|
369
369
|
return one["kind"] === "webhook" || ((one["kind"] === "github" || one["kind"] === "linear") && one["delivery"] === "webhook");
|
|
370
370
|
}
|
|
371
|
+
/** Whether a trigger brings in items from anyone (chat-management.md §0a.3): GitHub issues or pull requests from anyone,
|
|
372
|
+
* or mail from any sender. A widening: it takes a person's password, on every road that adds one. */
|
|
373
|
+
export function admitsAnyone(config) {
|
|
374
|
+
return (config.kind === "github" && config.from === "anyone") || (config.kind === "email" && config.sender === null);
|
|
375
|
+
}
|
|
376
|
+
/** The same, read leniently from a raw trigger before validation (so the form can ask for the password first). */
|
|
377
|
+
export function rawAdmitsAnyone(raw) {
|
|
378
|
+
if (raw === null || typeof raw !== "object")
|
|
379
|
+
return false;
|
|
380
|
+
const one = raw;
|
|
381
|
+
return (one["kind"] === "github" && one["from"] === "anyone") || (one["kind"] === "email" && (typeof one["sender"] !== "string" || one["sender"].trim() === ""));
|
|
382
|
+
}
|
|
371
383
|
export function takesDeliveries(config) {
|
|
372
384
|
return config.kind === "webhook" || ((config.kind === "github" || config.kind === "linear") && config.delivery === "webhook");
|
|
373
385
|
}
|
|
@@ -495,6 +507,9 @@ export function addFlowTriggerTo(store, flow, raw, actor, now, dir, options = {}
|
|
|
495
507
|
return { ok: false, message: "Webhook addresses are set up on the console." };
|
|
496
508
|
if (takesDeliveries(config) && config.kind !== "webhook" && options.ingress !== "proved")
|
|
497
509
|
return { ok: false, message: "Webhook delivery is set up on the flow's trigger panel, with your password." };
|
|
510
|
+
// Items from anyone come in only on a person's proof (chat-management.md §0a.3): never from a card, an import or a template alone.
|
|
511
|
+
if (admitsAnyone(config) && options.ingress !== "proved" && options.widening !== "proved")
|
|
512
|
+
return { ok: false, message: "A trigger that brings in items from anyone takes your password. Add it on the flow's Triggers panel." };
|
|
498
513
|
// v126: a webhook made where no one is shown its address (the gallery, an import) is made with none: its first
|
|
499
514
|
// set-up or New address makes one and shows it. A webhook made on its panel is shown its address now.
|
|
500
515
|
const token = takesDeliveries(config) && options.ingress === "proved" && (options.shown !== false || config.kind !== "webhook") ? randomBytes(24).toString("base64url") : null;
|
package/dist/flows-cli.js
CHANGED
|
@@ -21,7 +21,8 @@ import { deciderOf, FLOW_KIND_WORDS, FLOW_TEMPLATES, flowDigest, flowFromSteps,
|
|
|
21
21
|
import { prepareUnattendedCandidates, activeUnattendedGrant, parseUnattendedAsk, revokeUnattended, signUnattended, unattendedViewOf, unattendedWords, untilWords, UNATTENDED_MAX_RUNS } from './flow-unattended.js';
|
|
22
22
|
import { MODE_MAX_DAYS } from './modes.js';
|
|
23
23
|
import { verifyApproverByPassword } from './principal.js';
|
|
24
|
-
import { proveCredential } from './scope.js';
|
|
24
|
+
import { authenticateApprover, proveCredential } from './scope.js';
|
|
25
|
+
import { personProof } from './person-proof.js';
|
|
25
26
|
import { projectName } from './project.js';
|
|
26
27
|
const EXIT = { ok: 0, failed: 1, usage: 2, refused: 3 };
|
|
27
28
|
const json = { name: 'json', takesValue: false, meaning: 'answer with one machine envelope on stdout' };
|
|
@@ -157,6 +158,12 @@ export async function runFlowsCommand(positional, flags, context) {
|
|
|
157
158
|
store.recordAction({ at: context.clock().toISOString(), actor: who, repo: project, taskId: null, runId: null, action: command, outcome, source: 'request', detail: `command line${detail === null ? '' : ` · ${detail}`}` });
|
|
158
159
|
};
|
|
159
160
|
const refuse = (project, reason, message, code = EXIT.refused) => { record(project, 'refused', reason); return fail(reason, message, code); };
|
|
161
|
+
// A trigger from anyone widens what comes in (chat-management.md §0a.3): only the person's own proof counts, never a
|
|
162
|
+
// machine credential standing in for them (a remote run's API token, a lead's token), though both pass the check above.
|
|
163
|
+
const widening = (project) => {
|
|
164
|
+
const proved = authenticateApprover(store, acting.name, acting.token, project);
|
|
165
|
+
return proved.ok && personProof(proved.method) ? { widening: 'proved' } : {};
|
|
166
|
+
};
|
|
160
167
|
const settle = (project) => { try {
|
|
161
168
|
advanceFlows(store, project, context.clock(), { evidenceRoot: context.evidenceRoot });
|
|
162
169
|
}
|
|
@@ -243,11 +250,13 @@ export async function runFlowsCommand(positional, flags, context) {
|
|
|
243
250
|
if (!flags.has('yes'))
|
|
244
251
|
return preview(project, `Create the ${flowName} flow in ${projectName(project)}`, terms, { definition });
|
|
245
252
|
let id;
|
|
253
|
+
const proof = template?.trigger === undefined ? {} : widening(project);
|
|
246
254
|
try {
|
|
247
255
|
id = store.transact(() => {
|
|
248
256
|
const made = store.createFlow({ repo: project, name: flowName, definitionJson: JSON.stringify(definition), by: who }, now);
|
|
249
257
|
if (template?.trigger !== undefined) {
|
|
250
|
-
|
|
258
|
+
// A trigger from anyone only on the person's own proof (widening above); a machine credential's is refused.
|
|
259
|
+
const trigger = addFlowTriggerTo(store, store.getFlow(made), template.trigger, who, now, context.configDir, proof);
|
|
251
260
|
if (!trigger.ok)
|
|
252
261
|
throw new Error(trigger.message);
|
|
253
262
|
}
|
|
@@ -508,7 +517,8 @@ export async function runFlowsCommand(positional, flags, context) {
|
|
|
508
517
|
const ingress = rawTakesDeliveries(input.value);
|
|
509
518
|
if (ingress && !proveCredential(store, acting.name, acting.token).ok)
|
|
510
519
|
return refuse(flow.repo, 'unauthenticated', 'Making a webhook address takes your password, typed: pass --token.');
|
|
511
|
-
|
|
520
|
+
// A trigger from anyone only on the person's own proof (widening above): a machine credential's is refused.
|
|
521
|
+
const made = addFlowTriggerTo(store, flow, input.value, who, now, context.configDir, ingress ? { ingress: 'proved' } : widening(flow.repo));
|
|
512
522
|
if (!made.ok)
|
|
513
523
|
return refuse(flow.repo, 'invalid-trigger', made.message);
|
|
514
524
|
await prepareUnattendedCandidates(store, flow.id);
|
package/dist/guides.js
CHANGED
|
@@ -445,7 +445,13 @@ with a time limit.
|
|
|
445
445
|
|
|
446
446
|
## What it does, in order
|
|
447
447
|
|
|
448
|
-
1. **gate**:
|
|
448
|
+
1. **gate**: before filing, looks at the pull request's checks on the
|
|
449
|
+
branch's exact commit: a required check that failed stops here, before
|
|
450
|
+
anything is filed or sent for approval. Checks not started or still
|
|
451
|
+
running are named and waited for (GitHub not answering is waited out
|
|
452
|
+
too); if they don't finish within the gate's limit (60 min, \`--limit
|
|
453
|
+
gate=<min>\`), it stops here as well. A task already filed for the commit is kept and
|
|
454
|
+
not looked at again. Then it files task \`release-<version>-<sha7>\` pinned to the
|
|
449
455
|
branch's exact commit with \`--checks full\`, regardless of the project's default.
|
|
450
456
|
2. **approval**: waits for the owner to approve that exact scope
|
|
451
457
|
(\`toolroll task approve <id> --digest <d>\`, or the console). A
|
|
@@ -499,6 +505,9 @@ instead of making them twice.
|
|
|
499
505
|
checks and obtain a passing result; Quick and Off cannot authorize a release.
|
|
500
506
|
- \`check-failed\`: fix the branch and release the new commit with \`--new\`.
|
|
501
507
|
- \`ci-failed\`: fix the failed GitHub check, then rerun. Nothing was merged.
|
|
508
|
+
A stop at the gate comes before the task is filed, so nothing was filed
|
|
509
|
+
either: a fix pushed as a new commit is released with \`--new\`; after
|
|
510
|
+
rerunning the failed job on GitHub, run the same command again.
|
|
502
511
|
- \`publish-failed\` / \`homebrew-failed\`: fix the failed workflow before
|
|
503
512
|
resuming. A failed tag check cannot finish publication or update Homebrew.
|
|
504
513
|
- \`base-moved\`: main gained a commit the branch doesn't have, so the
|
package/dist/invoke.d.ts
CHANGED
|
@@ -190,3 +190,16 @@ export declare function checkoutRunner(store: Store, repo: string, checkout: str
|
|
|
190
190
|
export declare function checkoutSandboxOf(store: Store, repo: string, runIds: readonly number[]): LaunchSandbox;
|
|
191
191
|
/** The digest of a launch's whole fence (its denied paths and grants, canonical), as stamped on its run (C11). */
|
|
192
192
|
export declare function fenceDigest(fence: readonly string[], grants: FenceGrants): string;
|
|
193
|
+
/** What a Claude turn's own stream says about how it billed (v105). */
|
|
194
|
+
export type ClaudeBillingSeen = {
|
|
195
|
+
keySource: string | null;
|
|
196
|
+
model: string | null;
|
|
197
|
+
planWindows: boolean;
|
|
198
|
+
answered: boolean;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* One stream event's billing observation: the key source and model from the session's own init, plan windows from a
|
|
202
|
+
* rate-limit event, and whether the main query answered. The session's own events only (stream-scope.ts): a
|
|
203
|
+
* subagent's init, rate-limit event or result says nothing about this turn's billing.
|
|
204
|
+
*/
|
|
205
|
+
export declare function observeClaudeBilling(seen: ClaudeBillingSeen, event: Record<string, unknown>): void;
|
package/dist/invoke.js
CHANGED
|
@@ -44,6 +44,7 @@ import { buildCacheEnv, buildCacheFolder, grantFolder, writeAllowlist, WRITES_DE
|
|
|
44
44
|
import { proxyEnv, providerOnlyNetwork, repositoryEcosystems, resolveNetwork, startEgressProxy } from "./egress.js";
|
|
45
45
|
import { claudeConfigDir, claudeDefaults, codexDefaults, codexHomeDir, isolatedCodexHome } from "./provider-home.js";
|
|
46
46
|
import { billingOf, claudeBillingFrom, seenBilling } from "./spend.js";
|
|
47
|
+
import { primaryResultEvent, topLevelEvent } from "./stream-scope.js";
|
|
47
48
|
/** "Within seconds": how soon a silent failed run must end for its stderr to
|
|
48
49
|
* be read for the sign-in signal. */
|
|
49
50
|
const AUTH_EARLY_EXIT_MS = 30_000;
|
|
@@ -507,18 +508,7 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
|
507
508
|
...(workspace === null ? {} : { cwd: workspace }),
|
|
508
509
|
// v105: how the CLI really billed this turn (its key source, its plan's windows), read off its own stream.
|
|
509
510
|
onStreamEvent: event => {
|
|
510
|
-
|
|
511
|
-
if (typeof event["apiKeySource"] === "string")
|
|
512
|
-
billingSeen.keySource = event["apiKeySource"].slice(0, 80);
|
|
513
|
-
if (typeof event["model"] === "string")
|
|
514
|
-
billingSeen.model = event["model"].slice(0, 200);
|
|
515
|
-
}
|
|
516
|
-
else if (event["type"] === "rate_limit_event") {
|
|
517
|
-
billingSeen.planWindows = true;
|
|
518
|
-
}
|
|
519
|
-
else if (event["type"] === "result" && event["is_error"] !== true && event["subtype"] === "success") {
|
|
520
|
-
billingSeen.answered = true;
|
|
521
|
-
}
|
|
511
|
+
observeClaudeBilling(billingSeen, event);
|
|
522
512
|
runOptions.onStreamEvent?.(event);
|
|
523
513
|
},
|
|
524
514
|
owner: runOwnerTag(store, runId),
|
|
@@ -1303,3 +1293,22 @@ export function checkoutSandboxOf(store, repo, runIds) {
|
|
|
1303
1293
|
export function fenceDigest(fence, grants) {
|
|
1304
1294
|
return createHash("sha256").update(`toolroll:fence:${canonicalJson({ fence: [...fence], grants: grants })}`).digest("hex").slice(0, 32);
|
|
1305
1295
|
}
|
|
1296
|
+
/**
|
|
1297
|
+
* One stream event's billing observation: the key source and model from the session's own init, plan windows from a
|
|
1298
|
+
* rate-limit event, and whether the main query answered. The session's own events only (stream-scope.ts): a
|
|
1299
|
+
* subagent's init, rate-limit event or result says nothing about this turn's billing.
|
|
1300
|
+
*/
|
|
1301
|
+
export function observeClaudeBilling(seen, event) {
|
|
1302
|
+
if (event["type"] === "system" && event["subtype"] === "init" && topLevelEvent(event)) {
|
|
1303
|
+
if (typeof event["apiKeySource"] === "string")
|
|
1304
|
+
seen.keySource = event["apiKeySource"].slice(0, 80);
|
|
1305
|
+
if (typeof event["model"] === "string")
|
|
1306
|
+
seen.model = event["model"].slice(0, 200);
|
|
1307
|
+
}
|
|
1308
|
+
else if (event["type"] === "rate_limit_event" && topLevelEvent(event)) {
|
|
1309
|
+
seen.planWindows = true;
|
|
1310
|
+
}
|
|
1311
|
+
else if (primaryResultEvent(event) && event["is_error"] !== true && event["subtype"] === "success") {
|
|
1312
|
+
seen.answered = true;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
package/dist/kits-ui.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { kitChecklist, kitInstalled, KITS } from "./kits.js";
|
|
2
2
|
import { SUBAGENT_TEMPLATES } from "./subagents.js";
|
|
3
|
-
import { html, postForm } from "./html.js";
|
|
3
|
+
import { html, postForm, reauth } from "./html.js";
|
|
4
4
|
const CHECK = html `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
|
|
5
5
|
const RING = html `<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>`;
|
|
6
6
|
export const KITS_CSS = `.kits{max-width:960px;min-width:0}.kits-lede{color:var(--so-muted);margin:.25rem 0 1.5rem;max-width:40rem}.kit-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}` +
|
|
@@ -35,7 +35,9 @@ export function kitPageHtml(store, kit, repo, dir, canSetUp, notice) {
|
|
|
35
35
|
const rows = steps.map(step => {
|
|
36
36
|
const go = step.done ? (step.href === null ? "" : html `<a class="kit-step-go" href="${step.href}">Open</a>`)
|
|
37
37
|
: step.action === "sample" || step.action === "github"
|
|
38
|
-
? (canSetUp ? postForm(`/kits/${kit.id}/${step.action}`,
|
|
38
|
+
? (canSetUp ? postForm(`/kits/${kit.id}/${step.action}`, step.action === "sample" ? html `<button>Try it</button>`
|
|
39
|
+
// Issues from anyone come in: it takes your password (an ordinary step-up; a session does under Passwordless).
|
|
40
|
+
: html `${reauth(html `<input type="password" name="password" autocomplete="current-password" aria-label="Your password" placeholder="Your password">`)}<button class="secondary">Bring them in</button>`, { hidden: { repo } }) : "")
|
|
39
41
|
: step.href === null ? "" : html `<a class="kit-step-go" href="${step.href}">${step.action === "connect" ? "Connect" : "Set up"}</a>`;
|
|
40
42
|
return html `<li data-step="${step.id}" data-done="${String(step.done)}">${step.done ? CHECK : RING}<span class="kit-step-said">${step.said}</span>${go}</li>`;
|
|
41
43
|
});
|
package/dist/kits.d.ts
CHANGED
|
@@ -53,9 +53,11 @@ export declare function kitInstalled(store: Store, kit: Kit, repo: string): {
|
|
|
53
53
|
* flow, and its button triggers; then its subagent may use whichever of the
|
|
54
54
|
* kit's tools the project already has (reading free, writing asking first).
|
|
55
55
|
*/
|
|
56
|
-
export declare function setUpKit(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, io?: ToolIo
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
export declare function setUpKit(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, io?: ToolIo, options?: {
|
|
57
|
+
widening?: "proved";
|
|
58
|
+
}): Promise<Done>;
|
|
59
|
+
/** The same, all at once and without its tools (a chat confirmation is never a grant's proof): its subagent, its flow and its buttons. */
|
|
60
|
+
export declare function setUpKitNow(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null): Done;
|
|
59
61
|
/** One line of a kit's checklist: done, or the next step with where to take it. */
|
|
60
62
|
export type KitStep = {
|
|
61
63
|
id: string;
|
|
@@ -76,7 +78,7 @@ export declare function addKitSample(store: Store, kit: Kit, repo: string, by: s
|
|
|
76
78
|
said: string;
|
|
77
79
|
};
|
|
78
80
|
/** Bring new issues from the project's GitHub repository into the kit's flow (issues from anyone, polled). */
|
|
79
|
-
export declare function addKitGithubTrigger(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null): {
|
|
81
|
+
export declare function addKitGithubTrigger(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, proved?: "proved" | null): {
|
|
80
82
|
ok: true;
|
|
81
83
|
said: string;
|
|
82
84
|
} | {
|
package/dist/kits.js
CHANGED
|
@@ -17,7 +17,7 @@ import { readEmailSettings } from "./email-settings.js";
|
|
|
17
17
|
import { mailboxReady } from "./mailbox.js";
|
|
18
18
|
import { projectToolsOf } from "./project-tools.js";
|
|
19
19
|
import { createSubagentFrom, nameOf } from "./subagent-admin.js";
|
|
20
|
-
import {
|
|
20
|
+
import { grantTool } from "./subagent-tools.js";
|
|
21
21
|
import { SUBAGENT_TEMPLATES } from "./subagents.js";
|
|
22
22
|
export const KITS = [
|
|
23
23
|
{
|
|
@@ -115,22 +115,23 @@ export function kitInstalled(store, kit, repo) {
|
|
|
115
115
|
* flow, and its button triggers; then its subagent may use whichever of the
|
|
116
116
|
* kit's tools the project already has (reading free, writing asking first).
|
|
117
117
|
*/
|
|
118
|
-
export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
|
|
118
|
+
export async function setUpKit(store, kit, repo, by, now, dir, io = {}, options = {}) {
|
|
119
119
|
const made = setUpKitNow(store, kit, repo, by, now, dir);
|
|
120
|
-
|
|
120
|
+
// Its tools only with the person's proof (chat-management.md §0a.3); without it, the checklist says to let it use each.
|
|
121
|
+
if (!made.ok || made.said.endsWith("already set up here.") || options.widening !== "proved")
|
|
121
122
|
return made;
|
|
122
123
|
const mate = store.getSubagent(made.subagent);
|
|
123
124
|
const present = new Set(projectToolsOf(store, repo).map(one => one.name));
|
|
124
125
|
for (const tool of kit.tools.filter(one => present.has(one.tool))) {
|
|
125
126
|
try {
|
|
126
|
-
await grantTool(store, mate, tool.tool, by, now, io);
|
|
127
|
+
await grantTool(store, mate, tool.tool, by, now, io, options);
|
|
127
128
|
}
|
|
128
129
|
catch { /* it can be given the tool on its page */ }
|
|
129
130
|
}
|
|
130
131
|
return made;
|
|
131
132
|
}
|
|
132
|
-
/** The same, all at once (a chat confirmation
|
|
133
|
-
export function setUpKitNow(store, kit, repo, by, now, dir
|
|
133
|
+
/** The same, all at once and without its tools (a chat confirmation is never a grant's proof): its subagent, its flow and its buttons. */
|
|
134
|
+
export function setUpKitNow(store, kit, repo, by, now, dir) {
|
|
134
135
|
const had = kitInstalled(store, kit, repo);
|
|
135
136
|
if (had !== null)
|
|
136
137
|
return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id, subagent: had.mate.id };
|
|
@@ -155,9 +156,6 @@ export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false)
|
|
|
155
156
|
const flow = store.getFlow(flowId);
|
|
156
157
|
for (const button of kit.buttons)
|
|
157
158
|
addFlowTriggerTo(store, flow, { kind: "button", label: button.label, questions: button.questions, zone: definition.start }, by, now, dir);
|
|
158
|
-
if (grantByName)
|
|
159
|
-
for (const tool of projectToolsOf(store, repo).filter(one => kit.tools.some(each => each.tool === one.name)))
|
|
160
|
-
grantListed(store, mate, tool, null, by, now);
|
|
161
159
|
return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId, subagent: mate.id };
|
|
162
160
|
}
|
|
163
161
|
/** What's set up and what's left, read fresh each time. */
|
|
@@ -205,11 +203,11 @@ export function addKitSample(store, kit, repo, by, now) {
|
|
|
205
203
|
return added.ok ? { ok: true, href: flowCardHref(set.flow.id, added.card) } : { ok: false, said: added.message };
|
|
206
204
|
}
|
|
207
205
|
/** Bring new issues from the project's GitHub repository into the kit's flow (issues from anyone, polled). */
|
|
208
|
-
export function addKitGithubTrigger(store, kit, repo, by, now, dir) {
|
|
206
|
+
export function addKitGithubTrigger(store, kit, repo, by, now, dir, proved = null) {
|
|
209
207
|
const set = kitInstalled(store, kit, repo);
|
|
210
208
|
const github = githubRepoOf(repo);
|
|
211
209
|
if (set === null || github === null)
|
|
212
210
|
return { ok: false, said: "This project isn't on GitHub." };
|
|
213
|
-
const made = addFlowTriggerTo(store, set.flow, { kind: "github", repo: github, watch: "issues", from: "anyone", delivery: "poll", zone: flowDefinitionOf(set.flow)?.start }, by, now, dir);
|
|
211
|
+
const made = addFlowTriggerTo(store, set.flow, { kind: "github", repo: github, watch: "issues", from: "anyone", delivery: "poll", zone: flowDefinitionOf(set.flow)?.start }, by, now, dir, proved === null ? {} : { widening: proved });
|
|
214
212
|
return made.ok ? { ok: true, said: `New issues on ${github} now come into ${kit.flowName}.` } : { ok: false, said: made.message };
|
|
215
213
|
}
|
package/dist/knowledge-ui.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type KnowledgeDraft, type KnowledgeSelection, type KnowledgeView } from './project-knowledge.js';
|
|
2
2
|
import { type Html } from './html.js';
|
|
3
|
-
|
|
3
|
+
/** A section as a frame (after ReUI's Frame, MIT): its heading on the raised well, its body one paper panel. The
|
|
4
|
+
* cards inside become hairline-divided rows of that panel, never cards in a card. */
|
|
5
|
+
export declare function framedSection(heading: string, body: Html | string, extra?: string): Html;
|
|
6
|
+
export declare const KNOWLEDGE_CSS = ".knowledge{max-width:780px;min-width:0;overflow-wrap:anywhere}.knowledge .card{padding:20px;min-width:0;margin:16px 0}.knowledge h2{margin:0 0 12px}.knowledge form{display:grid;gap:12px;margin:0}.knowledge label{display:grid;gap:6px}.knowledge textarea,.knowledge input,.knowledge select{box-sizing:border-box;width:100%;min-width:0;max-width:100%}.knowledge textarea{resize:vertical;min-height:110px}.knowledge button,.knowledge summary,.knowledge .button-link{min-height:44px}.knowledge button{justify-self:start;white-space:nowrap}.knowledge summary{padding:12px 0;cursor:pointer}.knowledge pre{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit}.knowledge .knowledge-actions{display:flex;gap:12px;flex-wrap:wrap}.knowledge .meta{font-size:.86rem}.knowledge .knowledge-source{display:block}.knowledge details+details{border-top:1px solid var(--so-line)}.memory-stats{display:flex;flex-wrap:wrap;gap:.4rem}.memory-stats span+span::before{content:\"\u00B7\";margin-right:.4rem}.knowledge.frame{margin:0 0 16px}.knowledge.frame>.frame-head h2,.knowledge.frame>summary.frame-head{margin:0;font-size:13px;font-weight:600;line-height:1.4}.knowledge.frame>summary.frame-head{min-height:44px;padding:4px 10px}.knowledge.frame>.frame-panel{min-width:0}.knowledge.frame>.frame-panel>:first-child{margin-top:0}.knowledge.frame>.frame-panel>:last-child{margin-bottom:0}.knowledge .frame-panel>details.card,.knowledge .frame-panel .learning .card{margin:0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.knowledge .frame-panel>details.card+details.card,.knowledge .frame-panel .learning .card+.card{border-top:1px solid var(--so-line);padding-top:4px}.knowledge.frame>summary.frame-head{justify-content:flex-start}.knowledge form.field-row{grid-template-columns:minmax(0,1fr) auto;align-items:end}.knowledge form.field-row>label,.knowledge form.field-row :is(input,select,button){margin:0}@media(max-width:600px){.knowledge .card{padding:16px}.knowledge h2{font-size:1.1rem}}";
|
|
4
7
|
export declare function knowledgeHtml(view: KnowledgeView, csrf: string, canManage: boolean, draft?: KnowledgeDraft, error?: string): Html;
|
|
5
8
|
export declare function knowledgeContextHtml(snapshot: KnowledgeSelection | null): Html | '';
|
|
6
9
|
import type { Decision, MemoryHit } from './project-memory.js';
|
package/dist/knowledge-ui.js
CHANGED
|
@@ -2,7 +2,12 @@ import { STALE_KNOWLEDGE } from './project-knowledge.js';
|
|
|
2
2
|
import { html, postForm } from './html.js';
|
|
3
3
|
// Hidden fields as they always were: a missing value is an empty field.
|
|
4
4
|
const fields = (data) => Object.fromEntries(Object.entries(data).map(([k, v]) => [k, String(v ?? '')]));
|
|
5
|
-
|
|
5
|
+
/** A section as a frame (after ReUI's Frame, MIT): its heading on the raised well, its body one paper panel. The
|
|
6
|
+
* cards inside become hairline-divided rows of that panel, never cards in a card. */
|
|
7
|
+
export function framedSection(heading, body, extra = '') {
|
|
8
|
+
return html `<section class="${`knowledge frame${extra ? ` ${extra}` : ''}`}"><div class="frame-head"><h2>${heading}</h2></div><div class="frame-panel">${body}</div></section>`;
|
|
9
|
+
}
|
|
10
|
+
export const KNOWLEDGE_CSS = `.knowledge{max-width:780px;min-width:0;overflow-wrap:anywhere}.knowledge .card{padding:20px;min-width:0;margin:16px 0}.knowledge h2{margin:0 0 12px}.knowledge form{display:grid;gap:12px;margin:0}.knowledge label{display:grid;gap:6px}.knowledge textarea,.knowledge input,.knowledge select{box-sizing:border-box;width:100%;min-width:0;max-width:100%}.knowledge textarea{resize:vertical;min-height:110px}.knowledge button,.knowledge summary,.knowledge .button-link{min-height:44px}.knowledge button{justify-self:start;white-space:nowrap}.knowledge summary{padding:12px 0;cursor:pointer}.knowledge pre{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit}.knowledge .knowledge-actions{display:flex;gap:12px;flex-wrap:wrap}.knowledge .meta{font-size:.86rem}.knowledge .knowledge-source{display:block}.knowledge details+details{border-top:1px solid var(--so-line)}.memory-stats{display:flex;flex-wrap:wrap;gap:.4rem}.memory-stats span+span::before{content:"·";margin-right:.4rem}.knowledge.frame{margin:0 0 16px}.knowledge.frame>.frame-head h2,.knowledge.frame>summary.frame-head{margin:0;font-size:13px;font-weight:600;line-height:1.4}.knowledge.frame>summary.frame-head{min-height:44px;padding:4px 10px}.knowledge.frame>.frame-panel{min-width:0}.knowledge.frame>.frame-panel>:first-child{margin-top:0}.knowledge.frame>.frame-panel>:last-child{margin-bottom:0}.knowledge .frame-panel>details.card,.knowledge .frame-panel .learning .card{margin:0;padding:0;border:0;border-radius:0;background:none;box-shadow:none}.knowledge .frame-panel>details.card+details.card,.knowledge .frame-panel .learning .card+.card{border-top:1px solid var(--so-line);padding-top:4px}.knowledge.frame>summary.frame-head{justify-content:flex-start}.knowledge form.field-row{grid-template-columns:minmax(0,1fr) auto;align-items:end}.knowledge form.field-row>label,.knowledge form.field-row :is(input,select,button){margin:0}@media(max-width:600px){.knowledge .card{padding:16px}.knowledge h2{font-size:1.1rem}}`;
|
|
6
11
|
export function knowledgeHtml(view, csrf, canManage, draft, error) {
|
|
7
12
|
const manage = canManage && !!csrf;
|
|
8
13
|
const base = { repo: view.repo, identity: view.identity, revision: view.revision };
|
|
@@ -18,7 +23,7 @@ export function knowledgeHtml(view, csrf, canManage, draft, error) {
|
|
|
18
23
|
}
|
|
19
24
|
const instructions = html `<pre class="knowledge-instructions">${view.knowledge.instructions || 'No project instructions yet.'}</pre>`;
|
|
20
25
|
const editor = manage ? html `<details class="knowledge-editor"${draft?.instructions !== undefined ? html ` open` : ''}><summary>${view.knowledge.instructions ? 'Edit instructions' : 'Add instructions'}</summary>${form('instructions', html `<textarea aria-label="Project instructions" name="instructions" rows="5" maxlength="4000" placeholder="Goals, preferences, and what good work looks like…">${draft?.instructions ?? view.knowledge.instructions}</textarea><button>Save instructions</button>`)}</details>` : '';
|
|
21
|
-
return html `<section class="knowledge knowledge-library">${error ? html `<p class="problem" role="alert">${error}</p>` : ''}
|
|
26
|
+
return html `<section class="knowledge knowledge-library">${error ? html `<p class="problem" role="alert">${error}</p>` : ''}${framedSection('Instructions', html `${instructions}${editor}`, 'knowledge-intro')}${framedSection('References', html `${refs.length ? refs : html `<p class="meta">Add a spec, design guide, or useful project document.</p>`}${manage ? html `<details class="card"${draft?.title && !draft.id ? html ` open` : ''}><summary>Add reference</summary>${form('save', referenceFields(draft?.id ? {} : draft ?? {}))}</details>` : ''}`)}${history.length ? html `<details class="knowledge frame"><summary class="frame-head">Change history</summary><div class="frame-panel"><ol>${history}</ol></div></details>` : ''}</section>`;
|
|
22
27
|
}
|
|
23
28
|
export function knowledgeContextHtml(snapshot) {
|
|
24
29
|
if (!snapshot)
|
|
@@ -32,17 +37,17 @@ export function decisionsHtml(repo, decisions, csrf, canManage, draft, error) {
|
|
|
32
37
|
const form = (action, body, extra = {}) => postForm('/settings/knowledge/decision', body, { hidden: fields({ ...base, action, ...extra }) });
|
|
33
38
|
const rows = decisions.map(d => html `<details class="card decision" id="decision-${d.id}"><summary><strong>${d.claim}</strong> <span class="meta">#${d.id} · ${d.decidedBy} · ${d.decidedAt.slice(0, 10)}</span></summary><p>${d.why}</p><p class="meta">${d.sourceKind}${d.sourceRef ? ` · ${d.sourceRef}` : ''}${d.supersedes === null ? '' : ` · replaces #${d.supersedes}`}</p>${manage ? form('retire', html `<label>Why it no longer holds<input name="reason" maxlength="500" required></label><button type="submit">Retire</button>`, { decision: d.id }) : ''}</details>`);
|
|
34
39
|
const add = manage ? html `<details class="knowledge-editor"${draft?.claim !== undefined ? html ` open` : ''}><summary>Record a decision</summary>${form('record', html `<label>The choice, in one sentence<input name="claim" maxlength="240" value="${draft?.claim}" required></label><label>Why<textarea name="why" rows="3" maxlength="2000" required>${draft?.why}</textarea></label><button type="submit">Record</button>`)}</details>` : '';
|
|
35
|
-
return html
|
|
40
|
+
return framedSection('Decisions', html `${error ? html `<p class="problem" role="alert">${error}</p>` : ''}${rows.length ? rows : html `<p class="meta">No decisions yet. The lead suggests one when a conversation settles a choice.</p>`}${add}`);
|
|
36
41
|
}
|
|
37
42
|
export function memorySearchHtml(repo, query, hits) {
|
|
38
43
|
const kind = (h) => h.kind === 'decision' ? html `<a href="#decision-${h.ref}">decision #${h.ref}</a>` : h.kind === 'conversation' ? 'conversation' : h.kind;
|
|
39
44
|
const results = hits === null ? '' : hits.length === 0 ? html `<p class="meta">Nothing in project memory matches.</p>` : html `<ul class="memory-hits">${hits.map(h => html `<li><strong>${kind(h)}</strong> · ${h.title}<br><span class="meta">${h.snippet}</span></li>`)}</ul>`;
|
|
40
|
-
return html `<
|
|
45
|
+
return framedSection('Search project memory', html `<form class="field-row" method="get" action="/settings/knowledge"><input type="hidden" name="repo" value="${repo}"><label><span class="visually-hidden">Search project memory</span><input name="memory" value="${query}" maxlength="300" placeholder="Search decisions, references, lessons…" type="search" autocomplete="off"></label><button type="submit">Search</button></form>${results}`, 'memory-search');
|
|
41
46
|
}
|
|
42
47
|
export function proposalsHtml(repo, proposals, status, csrf, canManage) {
|
|
43
48
|
const manage = canManage && !!csrf;
|
|
44
49
|
const line = status === null ? '' : html `<p class="meta memory-stats"><span>${status.sessions.analyzed}/${status.sessions.total} sessions read${status.sessions.failed ? ` · ${status.sessions.failed} failed` : ''}</span><span>${status.openGaps} open gap${status.openGaps === 1 ? '' : 's'}</span><span>Instructions ${status.instructionBytes}/${status.budgetBytes} bytes</span></p><details class="settings-more"><summary>Read new sessions</summary><p class="meta">Run <code>toolroll memory propose --repo ${repo}</code> on this computer.</p></details>`;
|
|
45
50
|
const decide = (p, decision, label) => postForm('/settings/knowledge/proposal', html `<button type="submit">${label}</button>`, { attrs: { class: 'inline' }, hidden: fields({ repo, proposal: p.id, decision }) });
|
|
46
51
|
const cards = proposals.map(p => html `<details class="card proposal" open><summary><strong>${p.title}</strong> <span class="meta">#${p.id} · ${p.kind} · ${p.sessions} sessions</span></summary><p>${p.rationale}</p>${p.beforeText ? html `<pre class="knowledge-instructions">- ${p.beforeText}</pre>` : ''}${p.afterText ? html `<pre class="knowledge-instructions">+ ${p.afterText}</pre>` : ''}<ul class="memory-evidence">${p.evidence.map(ev => html `<li>“${ev.quote.slice(0, 200)}” <span class="meta">${ev.session.split(':')[0] ?? ''}</span></li>`)}</ul>${manage ? html `${decide(p, 'accept', 'Accept')} ${decide(p, 'reject', 'Reject')}` : ''}</details>`);
|
|
47
|
-
return
|
|
52
|
+
return framedSection('Proposed from sessions', html `${line}${cards.length ? cards : html `<p class="meta">Nothing to review. Suggestions appear when the same mistake shows up in two sessions.</p>`}`);
|
|
48
53
|
}
|
package/dist/lead-progress.d.ts
CHANGED
|
@@ -20,11 +20,14 @@ export type LeadProgress = {
|
|
|
20
20
|
step: number;
|
|
21
21
|
id: string;
|
|
22
22
|
outcome: LeadToolOutcome;
|
|
23
|
-
}
|
|
23
|
+
}
|
|
24
|
+
/** `final`: the provider's finished answer for this step, sent once and secret-checked whole. */
|
|
25
|
+
| {
|
|
24
26
|
kind: "text";
|
|
25
27
|
turn: number;
|
|
26
28
|
step: number;
|
|
27
29
|
text: string;
|
|
30
|
+
final?: true;
|
|
28
31
|
};
|
|
29
32
|
export type LeadToolOutcome = {
|
|
30
33
|
state: "succeeded";
|
package/dist/lead-progress.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Live progress of one lead turn (chat streaming): what the lead is doing
|
|
2
2
|
* and the reply as it is written. Nothing here is durable or authoritative —
|
|
3
3
|
* the finished turn's saved message and cards remain the record. */
|
|
4
|
+
import { topLevelEvent } from "./stream-scope.js";
|
|
4
5
|
/** Plain words for each tool the lead uses, as the person watching reads them. */
|
|
5
6
|
const TOOL_LABELS = {
|
|
6
7
|
get_brief: "Catching up on your work",
|
|
@@ -127,7 +128,8 @@ export function claudeStreamReader(onText, capBytes = 1_048_576) {
|
|
|
127
128
|
catch {
|
|
128
129
|
continue;
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
+
// A subagent's frames (parent_tool_use_id set) are not the lead's reply: they never start, feed or end it.
|
|
132
|
+
if (!record(event) || !topLevelEvent(event) || event["type"] !== "stream_event" || !record(event["event"]))
|
|
131
133
|
continue;
|
|
132
134
|
const inner = event["event"];
|
|
133
135
|
if (inner["type"] === "content_block_start") {
|
package/dist/lead.js
CHANGED
|
@@ -486,6 +486,10 @@ export async function runLeadTurn(input) {
|
|
|
486
486
|
return fail("malformed-reply", LEAD_FAILURE_COPY.unreadableUnknownCost, true);
|
|
487
487
|
}
|
|
488
488
|
const answer = result.answer;
|
|
489
|
+
// The streamed reply's finished text, once, secret-checked whole: only now may its last word show live.
|
|
490
|
+
if (!direct && input.onProgress !== undefined && !controller.signal.aborted && answer.text !== "" && scanForSecrets(answer.text).length === 0) {
|
|
491
|
+
progress({ kind: "text", turn: turnId, step: steps, text: answer.text, final: true });
|
|
492
|
+
}
|
|
489
493
|
// Usage that cannot be true — more input tokens than bytes sent — is a
|
|
490
494
|
// malformed reply with unknown cost, never a number to settle by.
|
|
491
495
|
if (direct && answer.tokensIn > requestBytes) {
|
package/dist/live-bus.d.ts
CHANGED
|
@@ -128,6 +128,8 @@ export type LiveRoomOptions = {
|
|
|
128
128
|
/** A room whose read is costly reads at most once per this window after a signal; every viewer is still re-proved
|
|
129
129
|
* the moment a signal arrives (and again at the read). */
|
|
130
130
|
readSettleMs?: number;
|
|
131
|
+
/** Told when a room's last viewer has left and the room is gone (state kept per room can go with it). */
|
|
132
|
+
onEmpty?: (key: unknown) => void;
|
|
131
133
|
};
|
|
132
134
|
/**
|
|
133
135
|
* Rooms of open pages that hear the bus. `presence` says who else is here, as
|
package/dist/live-bus.js
CHANGED
|
@@ -309,6 +309,7 @@ export function createLiveRooms(fingerprint, presence, options = {}) {
|
|
|
309
309
|
if (room.reading !== null)
|
|
310
310
|
clearTimeout(room.reading);
|
|
311
311
|
rooms.delete(key);
|
|
312
|
+
options.onEmpty?.(key);
|
|
312
313
|
if (rooms.size === 0 && unsubscribe !== null) {
|
|
313
314
|
unsubscribe();
|
|
314
315
|
unsubscribe = null;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type LiveRecord } from "./live-records.js";
|
|
2
|
+
export declare const CURSOR_CAP: number;
|
|
3
|
+
/** Longer than any real key block: past this much of a stream's text with no END marker, its key state is known again. */
|
|
4
|
+
export declare const UNKNOWN_KEY_BYTES: number;
|
|
5
|
+
export type ChildView = {
|
|
6
|
+
k: "child";
|
|
7
|
+
s: number;
|
|
8
|
+
at?: string;
|
|
9
|
+
parent: string | null;
|
|
10
|
+
via?: number;
|
|
11
|
+
desc?: string;
|
|
12
|
+
} | {
|
|
13
|
+
k: "end";
|
|
14
|
+
s: number;
|
|
15
|
+
at?: string;
|
|
16
|
+
ok: boolean | null;
|
|
17
|
+
} | {
|
|
18
|
+
k: "over";
|
|
19
|
+
}
|
|
20
|
+
/** The file stopped at its size cap: nothing more will be written. */
|
|
21
|
+
| {
|
|
22
|
+
k: "cap";
|
|
23
|
+
} | (LiveRecord & {
|
|
24
|
+
s: number;
|
|
25
|
+
});
|
|
26
|
+
export type ChildWindow = {
|
|
27
|
+
ok: true;
|
|
28
|
+
records: ChildView[];
|
|
29
|
+
cursor: string;
|
|
30
|
+
/** A cursor was given but didn't hold (forged, stale, another run's or file's): this read started over. */
|
|
31
|
+
restarted: boolean;
|
|
32
|
+
/** Output before this read's start isn't shown (the read began at a checkpoint, or waits for one). */
|
|
33
|
+
earlier: boolean;
|
|
34
|
+
/** Records that failed a check: counted, never shown. */
|
|
35
|
+
quarantined: number;
|
|
36
|
+
/** Lines too long to show that were skipped. */
|
|
37
|
+
skipped: number;
|
|
38
|
+
/** More is already written: read again. */
|
|
39
|
+
more: boolean;
|
|
40
|
+
} | {
|
|
41
|
+
ok: false;
|
|
42
|
+
reason: "missing" | "unavailable";
|
|
43
|
+
};
|
|
44
|
+
/** Seal a reader's state under the caller's key (the same form for the run's own file and its subagents'). */
|
|
45
|
+
export declare function sealCursor(state: object, key: Buffer): string;
|
|
46
|
+
/** A sealed state's object when the seal holds and it is within the cap; null otherwise (the caller re-checks it). */
|
|
47
|
+
export declare function openCursor(cursor: string, key: Buffer): Record<string, unknown> | null;
|
|
48
|
+
/** One window of a run's subagents' file. `commands` is the run's own captured answer (store.ts runLiveCommands). */
|
|
49
|
+
export declare function readChildWindow(root: string, runId: number, cursor: string | null, options: {
|
|
50
|
+
key: Buffer;
|
|
51
|
+
commands: boolean;
|
|
52
|
+
}): ChildWindow;
|