toolroll 0.9.62 → 0.9.65
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 +80 -0
- package/README.md +66 -8
- package/dist/acp-agents.d.ts +43 -0
- package/dist/acp-agents.js +62 -0
- package/dist/acp-attest.d.ts +44 -0
- package/dist/acp-attest.js +269 -0
- package/dist/acp-check.d.ts +28 -0
- package/dist/acp-check.js +410 -0
- package/dist/acp-client.d.ts +163 -0
- package/dist/acp-client.js +738 -0
- package/dist/acp-inspect.d.ts +23 -0
- package/dist/acp-inspect.js +109 -0
- package/dist/agent-board.d.ts +19 -6
- package/dist/agent-board.js +30 -9
- package/dist/agent-fence.d.ts +203 -10
- package/dist/agent-fence.js +623 -41
- package/dist/agent-pulse.js +9 -2
- package/dist/agent-state-words.d.ts +19 -0
- package/dist/agent-state-words.js +20 -0
- package/dist/agent-state.d.ts +65 -0
- package/dist/agent-state.js +229 -0
- package/dist/agentconfig.js +36 -13
- package/dist/agents-board.d.ts +34 -2
- package/dist/agents-board.js +110 -1
- package/dist/approval-rules-ui.js +2 -2
- package/dist/assignment-delivery.d.ts +14 -0
- package/dist/assignment-delivery.js +13 -0
- package/dist/assignment.js +13 -0
- package/dist/attest.js +7 -0
- package/dist/backup-ui.js +2 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +48 -0
- package/dist/browser/terminal.css +37 -0
- package/dist/browser/terminal.js +117 -0
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +42 -38
- package/dist/browser-shell.js +6 -3
- package/dist/browser-workspace.d.ts +263 -1
- package/dist/browser-workspace.js +60 -0
- package/dist/builder.d.ts +6 -1
- package/dist/builder.js +34 -13
- package/dist/chat-actions.d.ts +13 -1
- package/dist/chat-actions.js +199 -17
- package/dist/chat-ask.d.ts +4 -0
- package/dist/chat-ask.js +14 -0
- package/dist/chat-channel.js +20 -0
- package/dist/chat-continuity.js +5 -4
- package/dist/chat-decide.d.ts +0 -14
- package/dist/chat-decide.js +4 -1
- package/dist/chat-delivery.d.ts +9 -0
- package/dist/chat-delivery.js +45 -6
- package/dist/chat-messages.d.ts +8 -0
- package/dist/chat-messages.js +36 -4
- package/dist/check-levels-ui.js +2 -2
- package/dist/check-levels.js +3 -0
- package/dist/checkout-proof.d.ts +28 -0
- package/dist/checkout-proof.js +62 -0
- package/dist/claim.d.ts +12 -0
- package/dist/claim.js +20 -1
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +5 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +24 -2
- package/dist/coding-ui.js +6 -4
- package/dist/console-instance.d.ts +124 -0
- package/dist/console-instance.js +337 -0
- package/dist/contracts/chat-actions.d.ts +16 -0
- package/dist/contracts/chat-actions.js +4 -0
- package/dist/contracts/chat-content.d.ts +19 -0
- package/dist/contracts/chat-content.js +6 -0
- package/dist/contracts/cli.d.ts +8 -0
- package/dist/contracts/cli.js +3 -1
- package/dist/contracts/console-api.d.ts +30 -4
- package/dist/contracts/console-api.js +13 -4
- package/dist/contracts/flow-card.d.ts +4 -0
- package/dist/contracts/flow-card.js +3 -0
- package/dist/contracts/flow.d.ts +46 -0
- package/dist/contracts/flow.js +11 -2
- package/dist/contracts/integration-metadata.d.ts +2 -1
- package/dist/contracts/integration-metadata.js +2 -1
- package/dist/contracts/lead-tools.d.ts +92 -0
- package/dist/contracts/lead-tools.js +13 -0
- package/dist/contracts/provider.d.ts +11 -1
- package/dist/contracts/provider.js +1 -1
- package/dist/contracts/route.d.ts +76 -1
- package/dist/contracts/route.js +1 -1
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/control-setup.d.ts +1 -1
- package/dist/control-ui.js +2 -2
- package/dist/converse.d.ts +16 -0
- package/dist/converse.js +32 -4
- package/dist/demo-chat.js +4 -4
- package/dist/demo.js +2 -2
- package/dist/desktop-update-gate.js +6 -0
- package/dist/discord-chat.js +4 -1
- package/dist/dispatch.js +7 -4
- package/dist/doctor.d.ts +53 -0
- package/dist/doctor.js +427 -0
- package/dist/egress-bridge.d.ts +17 -0
- package/dist/egress-bridge.js +111 -0
- package/dist/egress.d.ts +119 -0
- package/dist/egress.js +423 -0
- package/dist/evidence-pack.d.ts +1 -1
- package/dist/evidence-pack.js +2 -2
- package/dist/exec.d.ts +81 -0
- package/dist/exec.js +368 -9
- package/dist/export-ui.js +2 -2
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +4 -15
- package/dist/flow-engine.js +35 -11
- package/dist/flow-gallery-ui.js +2 -2
- package/dist/flow-gallery.js +27 -8
- package/dist/flow-share.js +16 -5
- package/dist/flow-triggers.d.ts +84 -11
- package/dist/flow-triggers.js +554 -87
- package/dist/flow-unattended-words.d.ts +28 -0
- package/dist/flow-unattended-words.js +50 -0
- package/dist/flow-unattended.d.ts +227 -0
- package/dist/flow-unattended.js +776 -0
- package/dist/flows-cli.d.ts +28 -1
- package/dist/flows-cli.js +63 -12
- package/dist/flows-ui.d.ts +2 -3
- package/dist/flows-ui.js +56 -5
- package/dist/flows.d.ts +4 -0
- package/dist/flows.js +9 -1
- package/dist/google-mail.d.ts +1 -0
- package/dist/google-mail.js +61 -18
- package/dist/html.d.ts +9 -0
- package/dist/html.js +11 -0
- package/dist/integrations-ui.d.ts +2 -1
- package/dist/integrations-ui.js +6 -3
- package/dist/integrations.d.ts +17 -3
- package/dist/integrations.js +38 -18
- package/dist/invoke.d.ts +17 -0
- package/dist/invoke.js +510 -182
- package/dist/keys.d.ts +18 -14
- package/dist/keys.js +5 -0
- package/dist/landed-cleanup.d.ts +3 -2
- package/dist/landed-cleanup.js +12 -5
- package/dist/lead-capabilities.js +3 -2
- package/dist/lead-contract.js +2 -0
- package/dist/lead-doors.d.ts +2 -0
- package/dist/lead-doors.js +7 -0
- package/dist/lead-follow.d.ts +2 -0
- package/dist/lead-follow.js +58 -2
- package/dist/lead-progress.js +1 -0
- package/dist/lead-queue-runtime.d.ts +46 -0
- package/dist/lead-queue-runtime.js +191 -0
- package/dist/lead-queue.d.ts +104 -0
- package/dist/lead-queue.js +187 -0
- package/dist/lead-sweep.d.ts +67 -0
- package/dist/lead-sweep.js +99 -0
- package/dist/lead-tools.d.ts +22 -0
- package/dist/lead-tools.js +163 -4
- package/dist/lead.d.ts +13 -5
- package/dist/lead.js +89 -29
- package/dist/limits-ui.d.ts +8 -1
- package/dist/limits-ui.js +26 -0
- package/dist/live-bus.d.ts +8 -0
- package/dist/live-bus.js +40 -4
- package/dist/live-detail.d.ts +27 -0
- package/dist/live-detail.js +57 -0
- package/dist/live-notes.d.ts +20 -0
- package/dist/live-notes.js +47 -0
- package/dist/live-records.d.ts +140 -0
- package/dist/live-records.js +935 -0
- package/dist/live-terminal.d.ts +258 -0
- package/dist/live-terminal.js +649 -0
- package/dist/live.d.ts +38 -27
- package/dist/live.js +142 -124
- package/dist/mcp-connect.d.ts +8 -7
- package/dist/mcp-connect.js +49 -51
- package/dist/mcp-oauth.d.ts +6 -3
- package/dist/mcp-oauth.js +8 -4
- package/dist/memory-pass.js +2 -2
- package/dist/mobile-viewport.d.ts +5 -1
- package/dist/mobile-viewport.js +18 -2
- package/dist/model-catalog.js +4 -0
- package/dist/monitoring-ui.js +2 -2
- package/dist/needs-you.d.ts +1 -1
- package/dist/needs-you.js +11 -4
- package/dist/oauth-run-token.d.ts +51 -0
- package/dist/oauth-run-token.js +127 -0
- package/dist/operate.d.ts +2 -0
- package/dist/operate.js +282 -18
- package/dist/passwordless.d.ts +23 -0
- package/dist/passwordless.js +26 -0
- package/dist/peek-cli.d.ts +12 -4
- package/dist/peek-cli.js +19 -11
- package/dist/phase-routing.js +10 -3
- package/dist/plan-auto.d.ts +11 -0
- package/dist/plan-auto.js +62 -11
- package/dist/planner-source.d.ts +3 -0
- package/dist/planner-source.js +14 -0
- package/dist/planner.d.ts +2 -1
- package/dist/planner.js +7 -1
- package/dist/policy-ui.js +2 -2
- package/dist/policy.d.ts +1 -20
- package/dist/policy.js +29 -2
- package/dist/process-recovery-eligibility.d.ts +1 -1
- package/dist/project-access.d.ts +3 -0
- package/dist/project-access.js +3 -0
- package/dist/project-delete-ui.d.ts +12 -0
- package/dist/project-delete-ui.js +41 -9
- package/dist/project-delete.d.ts +3 -1
- package/dist/project-delete.js +12 -6
- package/dist/project-file.d.ts +3 -0
- package/dist/project-file.js +16 -0
- package/dist/project-memory.d.ts +3 -1
- package/dist/project-memory.js +57 -25
- package/dist/project-tools.d.ts +103 -27
- package/dist/project-tools.js +399 -100
- package/dist/provider-auth.d.ts +4 -3
- package/dist/provider-auth.js +5 -4
- package/dist/provider-connection.d.ts +2 -1
- package/dist/provider-connection.js +5 -0
- package/dist/provider-home.d.ts +22 -0
- package/dist/provider-home.js +99 -0
- package/dist/provider.d.ts +42 -5
- package/dist/provider.js +97 -19
- package/dist/pull-request-ui.js +2 -2
- package/dist/push.js +1 -1
- package/dist/quick-start.d.ts +24 -0
- package/dist/quick-start.js +127 -0
- package/dist/release.d.ts +3 -0
- package/dist/release.js +14 -8
- package/dist/remote-main.d.ts +4 -0
- package/dist/remote-main.js +3 -3
- package/dist/request-budget-ui.js +2 -2
- package/dist/result-review.d.ts +1 -1
- package/dist/result-review.js +9 -3
- package/dist/retention-ui.js +2 -2
- package/dist/retention.js +10 -1
- package/dist/run-efficiency.d.ts +73 -0
- package/dist/run-efficiency.js +228 -0
- package/dist/runner.d.ts +5 -1
- package/dist/runner.js +22 -1
- package/dist/scope.d.ts +171 -6
- package/dist/scope.js +251 -31
- package/dist/scout-net.d.ts +4 -1
- package/dist/scout-net.js +37 -12
- package/dist/scout.d.ts +20 -6
- package/dist/scout.js +27 -3
- package/dist/serve.js +258 -43
- package/dist/server/chat.d.ts +1 -1
- package/dist/server/chat.js +122 -31
- package/dist/server/chrome.d.ts +6 -1
- package/dist/server/chrome.js +18 -7
- package/dist/server/console.css +52 -38
- package/dist/server/flows.js +65 -13
- package/dist/server/guards.d.ts +1 -0
- package/dist/server/guards.js +23 -9
- package/dist/server/http.d.ts +7 -0
- package/dist/server/http.js +3 -2
- package/dist/server/live.d.ts +34 -1
- package/dist/server/live.js +39 -17
- package/dist/server/pages-assets.js +5 -0
- package/dist/server/pages.js +90 -13
- package/dist/server/people-tokens.d.ts +3 -0
- package/dist/server/people-tokens.js +55 -17
- package/dist/server/remote-hooks.js +12 -6
- package/dist/server/remote.d.ts +2 -1
- package/dist/server/remote.js +14 -5
- package/dist/server/render-chat.d.ts +12 -3
- package/dist/server/render-chat.js +47 -16
- package/dist/server/render-pages.d.ts +4 -1
- package/dist/server/render-pages.js +5 -4
- package/dist/server/render-people.d.ts +3 -2
- package/dist/server/render-people.js +3 -2
- package/dist/server/render-results.d.ts +18 -2
- package/dist/server/render-results.js +37 -28
- package/dist/server/render-settings.js +6 -5
- package/dist/server/render-tasks.d.ts +35 -10
- package/dist/server/render-tasks.js +110 -27
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.js +2 -1
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +49 -5
- package/dist/server/runtime.d.ts +34 -3
- package/dist/server/session.d.ts +25 -1
- package/dist/server/session.js +18 -4
- package/dist/server/settings.js +132 -13
- package/dist/server/sweeps.d.ts +4 -0
- package/dist/server/sweeps.js +58 -0
- package/dist/server/task-views.d.ts +2 -1
- package/dist/server/task-views.js +96 -6
- package/dist/server/tasks.js +320 -74
- package/dist/server/terminals.d.ts +31 -0
- package/dist/server/terminals.js +365 -0
- package/dist/setup-guide.js +3 -2
- package/dist/slack-chat.js +4 -1
- package/dist/spend-ui.js +3 -3
- package/dist/spend.js +2 -1
- package/dist/steer-capabilities.d.ts +32 -0
- package/dist/steer-capabilities.js +60 -0
- package/dist/steer-controls.d.ts +14 -0
- package/dist/steer-controls.js +36 -0
- package/dist/steer-status.js +2 -2
- package/dist/storage-sweep-child.js +3 -1
- package/dist/storage-sweep-process.d.ts +12 -5
- package/dist/storage-sweep-process.js +23 -6
- package/dist/storage-ui.js +4 -4
- package/dist/store-baseline.d.ts +1 -1
- package/dist/store-baseline.js +1 -1
- package/dist/store.d.ts +466 -19
- package/dist/store.js +1185 -63
- package/dist/subagent-desk.js +1 -1
- package/dist/subagent-tools.d.ts +1 -1
- package/dist/summary.js +3 -0
- package/dist/surface.js +6 -4
- package/dist/task-source.d.ts +58 -0
- package/dist/task-source.js +93 -0
- package/dist/team-leads.js +1 -1
- package/dist/teams-chat.js +4 -1
- package/dist/teardown-git.d.ts +14 -0
- package/dist/teardown-git.js +119 -0
- package/dist/telegram-lead.d.ts +3 -0
- package/dist/telegram-lead.js +44 -7
- package/dist/telegram.d.ts +11 -2
- package/dist/telegram.js +51 -17
- package/dist/terminal-catalog.d.ts +11 -0
- package/dist/terminal-catalog.js +129 -0
- package/dist/text-limits.d.ts +2 -0
- package/dist/text-limits.js +2 -0
- package/dist/tool-broker.d.ts +39 -0
- package/dist/tool-broker.js +370 -0
- package/dist/tool-launcher.js +5 -4
- package/dist/tool-secret-store.d.ts +170 -0
- package/dist/tool-secret-store.js +834 -0
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +14 -3
- package/dist/tools-ui.d.ts +7 -1
- package/dist/tools-ui.js +19 -10
- package/dist/webhook-limits.d.ts +23 -0
- package/dist/webhook-limits.js +30 -0
- package/dist/webhook-readers.d.ts +56 -0
- package/dist/webhook-readers.js +138 -0
- package/dist/webhook-signing.d.ts +27 -0
- package/dist/webhook-signing.js +119 -0
- package/dist/work-index.d.ts +29 -0
- package/dist/work-index.js +21 -5
- package/dist/workspace-revision.js +3 -1
- package/dist/worktree.d.ts +10 -4
- package/dist/worktree.js +70 -29
- package/package.json +13 -2
- package/scripts/provider-canary.mjs +210 -31
package/dist/chat-actions.d.ts
CHANGED
|
@@ -68,6 +68,16 @@ export declare const CHAT_ACTIONS: {
|
|
|
68
68
|
readonly protected: false;
|
|
69
69
|
readonly password: false;
|
|
70
70
|
};
|
|
71
|
+
readonly sweep_file: {
|
|
72
|
+
readonly label: "Start sweep";
|
|
73
|
+
readonly protected: true;
|
|
74
|
+
readonly password: true;
|
|
75
|
+
};
|
|
76
|
+
readonly sweep_cancel_rest: {
|
|
77
|
+
readonly label: "Cancel the rest";
|
|
78
|
+
readonly protected: false;
|
|
79
|
+
readonly password: false;
|
|
80
|
+
};
|
|
71
81
|
readonly flow_edit: {
|
|
72
82
|
readonly label: "Save flow";
|
|
73
83
|
readonly protected: false;
|
|
@@ -256,7 +266,7 @@ export declare function sharedActionReviewPath(id: number): string;
|
|
|
256
266
|
export declare function mintSharedActionReview(store: Store, who: VerifiedApprover, id: number, root: string, now: Date): {
|
|
257
267
|
payload: {
|
|
258
268
|
version: 1;
|
|
259
|
-
operation: "decision_record" | "decision_retire" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "subagent_answer" | "subagent_ask" | "subagent_create" | "subagent_memory" | "subagent_note" | "subagent_routine" | "subagent_soul" | "subagent_state" | "subagent_tools" | "subagent_undo" | "task_cancel" | "task_resume" | "tool_add" | "tool_remove";
|
|
269
|
+
operation: "decision_record" | "decision_retire" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "subagent_answer" | "subagent_ask" | "subagent_create" | "subagent_memory" | "subagent_note" | "subagent_routine" | "subagent_soul" | "subagent_state" | "subagent_tools" | "subagent_undo" | "sweep_cancel_rest" | "sweep_file" | "task_cancel" | "task_resume" | "tool_add" | "tool_remove";
|
|
260
270
|
request: {
|
|
261
271
|
[x: string]: unknown;
|
|
262
272
|
};
|
|
@@ -286,6 +296,8 @@ export declare function executeSharedAction(store: Store, who: VerifiedApprover,
|
|
|
286
296
|
said: string;
|
|
287
297
|
taskId: string | null;
|
|
288
298
|
href?: string;
|
|
299
|
+
awaiting?: true;
|
|
300
|
+
already?: true;
|
|
289
301
|
} | {
|
|
290
302
|
ok: false;
|
|
291
303
|
reason: "needs-confirm" | "stale" | "refused";
|
package/dist/chat-actions.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/** Shared, exact-state actions. Models may prepare; only the existing human
|
|
2
2
|
* confirmation door executes. Protected actions use a one-use review receipt. */
|
|
3
|
+
import { fileTaskProposal } from "./proposal.js";
|
|
4
|
+
import { heuristicSizing } from "./task-sizing.js";
|
|
5
|
+
import { cancelSweepRest, sweepOpen, sweepTable, VERDICT_RULE } from "./lead-sweep.js";
|
|
6
|
+
import { isAcpProfile } from "./acp-agents.js";
|
|
3
7
|
import { withActor } from "./actor.js";
|
|
4
8
|
import { TEXT_LIMITS } from "./text-limits.js";
|
|
5
9
|
import { publicChatText } from "./chat-display.js";
|
|
@@ -9,7 +13,7 @@ import { leadTurnKeepsProposals, verifiedAuthor } from "./store.js";
|
|
|
9
13
|
import { isVerifiedApprover, reproveApprover, } from "./principal.js";
|
|
10
14
|
import { changeSkills, importSkill, skillsView, skillsVersion, testSkill, validateSkill, } from "./project-skills.js";
|
|
11
15
|
import { changeKnowledge, knowledgeVersion, knowledgeView, } from "./project-knowledge.js";
|
|
12
|
-
import { approve, authenticateApprover, describeScope, scopeAuthorityOf, } from "./scope.js";
|
|
16
|
+
import { passwordlessSessionFor, approve, authenticateApprover, describeScope, scopeAuthorityOf, freshnessFor, spendingOnlyOnSuccess, ssoNowAvailable, withFreshness, } from "./scope.js";
|
|
13
17
|
import { readVerifiedArtifact, scanForSecrets } from "./evidence.js";
|
|
14
18
|
import { readAuthModeStrict } from "./keys.js";
|
|
15
19
|
import { parseProof } from "./proof.js";
|
|
@@ -86,6 +90,11 @@ export const CHAT_ACTIONS = {
|
|
|
86
90
|
flow_create: { label: "Create flow", protected: false, password: false },
|
|
87
91
|
// A starter flow: its zones and its trigger, switched on with one yes (flow-starters.ts).
|
|
88
92
|
flow_starter: { label: "Switch on", protected: false, password: false },
|
|
93
|
+
// Lead fan-out (docs/plans/lead-fan-out.md): files N tasks under one brief and approves each one's scope — the
|
|
94
|
+
// person's ceremony (protected, with the password), N ordinary approvals of exactly the scopes shown.
|
|
95
|
+
sweep_file: { label: "Start sweep", protected: true, password: true },
|
|
96
|
+
// Stops every item of a sweep not yet ready; finished results stay. An ordinary act, like cancelling a task.
|
|
97
|
+
sweep_cancel_rest: { label: "Cancel the rest", protected: false, password: false },
|
|
89
98
|
flow_edit: { label: "Save flow", protected: false, password: false },
|
|
90
99
|
flow_card_add: { label: "Add card", protected: false, password: false },
|
|
91
100
|
flow_card_move: { label: "Move card", protected: false, password: false },
|
|
@@ -175,10 +184,12 @@ function runtimeTerms(profile) {
|
|
|
175
184
|
? profile.approvalArgv === "yolo"
|
|
176
185
|
? "Full access: every tool is approved automatically (--approval-mode yolo)."
|
|
177
186
|
: "Edits are approved automatically; other tools are refused."
|
|
178
|
-
: profile
|
|
179
|
-
? "Full access:
|
|
180
|
-
:
|
|
181
|
-
|
|
187
|
+
: isAcpProfile(profile)
|
|
188
|
+
? "Full access inside the OS fence: the agent runs any tool it chooses, and nothing outside the fence is reachable."
|
|
189
|
+
: profile.sandboxMode === "danger-full-access"
|
|
190
|
+
? "Full access: approvals and sandbox are bypassed (--dangerously-bypass-approvals-and-sandbox)."
|
|
191
|
+
: "Commands use the workspace-write sandbox.";
|
|
192
|
+
return `${profile.provider} · ${profile.model}\n${permission}\n${isAcpProfile(profile) ? `${profile.timeoutSeconds} seconds per attempt, or ${profile.idleTimeoutSeconds} seconds silent` : `${profile.timeoutSeconds} seconds ${profile.timeoutKind === "idle" ? "without progress" : "per attempt"}`}${profile.provider === "claude" ? `; at most ${profile.maxTurns} turns` : ""}. Repairs: ${profile.repairModel}, ${profile.repairTimeoutSeconds} seconds${profile.provider === "claude" ? `, at most ${profile.repairMaxTurns} turns` : ""}.`;
|
|
182
193
|
}
|
|
183
194
|
function resultOf(store, task, run, repo) {
|
|
184
195
|
const ref = store.lookupRef(task);
|
|
@@ -263,7 +274,7 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
|
|
|
263
274
|
const stamp = hash({ operation, request, repo: "", state, terms, actor: who.name, generation: who.generation, ceiling: who.ceilingDigest });
|
|
264
275
|
return { version: SHARED_ACTION_VERSION, operation, request, repo: "", title, terms, stamp, state };
|
|
265
276
|
}
|
|
266
|
-
const task = operation.startsWith("skill_") || operation.startsWith("knowledge_") || operation.startsWith("decision_") || operation.startsWith("tool_") || operation.startsWith("flow_") || operation.startsWith("subagent_") || operation.startsWith("kit_")
|
|
277
|
+
const task = operation.startsWith("skill_") || operation.startsWith("knowledge_") || operation.startsWith("decision_") || operation.startsWith("tool_") || operation.startsWith("flow_") || operation.startsWith("subagent_") || operation.startsWith("kit_") || operation.startsWith("sweep_")
|
|
267
278
|
? null
|
|
268
279
|
: text(input, "task", 64);
|
|
269
280
|
const flowTarget = operation.startsWith("flow_") && operation !== "flow_create" && operation !== "flow_script_save" && operation !== "flow_starter" ? flowTargetOf(store, input) : null;
|
|
@@ -275,7 +286,8 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
|
|
|
275
286
|
const questionFlow = questionTarget === null ? null : store.getFlow(store.getFlowCard(questionTarget.card)?.flow ?? -1);
|
|
276
287
|
if (operation === "subagent_answer" && (questionTarget === null || questionFlow === null))
|
|
277
288
|
throw Error("That question isn't open any more.");
|
|
278
|
-
const repo =
|
|
289
|
+
const repo = operation === "sweep_cancel_rest" ? store.getSweep(integer(input, "sweep"))?.repo
|
|
290
|
+
: flowTarget !== null ? flowTarget.flow.repo : subagentTarget !== null ? subagentTarget.repo : questionFlow !== null ? questionFlow.repo : task === null ? text(input, "repo", 4096) : store.lookupRef(task)?.repo;
|
|
279
291
|
if (!repo)
|
|
280
292
|
throw Error("Choose an available project.");
|
|
281
293
|
requireActor(store, who, repo);
|
|
@@ -554,6 +566,50 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
|
|
|
554
566
|
terms.push(question.question, `Your answer: ${[choice?.label, said].filter(Boolean).join(" — ")}`);
|
|
555
567
|
}
|
|
556
568
|
}
|
|
569
|
+
else if (operation === "sweep_cancel_rest") {
|
|
570
|
+
// Exactly the items open now: if any settles or starts in between, the card is stale and a fresh one is needed.
|
|
571
|
+
const table = sweepTable(store, integer(input, "sweep"), now, who.repos);
|
|
572
|
+
if (table === null || table.repo !== repo)
|
|
573
|
+
throw Error("That sweep isn't one of yours.");
|
|
574
|
+
// Exactly the items as they stand now — each one's active task (a revision, after one), state, running attempt and
|
|
575
|
+
// last change: any change makes this card stale, so nothing is acted on that wasn't shown.
|
|
576
|
+
const open = sweepOpen(store, table, now);
|
|
577
|
+
if (open.length === 0)
|
|
578
|
+
throw Error("Nothing in that sweep is still unfinished.");
|
|
579
|
+
state = { open };
|
|
580
|
+
title = `Cancel the rest of ${table.title}`;
|
|
581
|
+
const running = open.filter(one => one.run !== null);
|
|
582
|
+
terms.push(`Cancels ${open.length - running.length} unfinished item${open.length - running.length === 1 ? "" : "s"}${running.length === 0 ? "" : ` and stops ${running.length} running build${running.length === 1 ? "" : "s"}`}: ${open.map(one => `${one.label} (${one.task})`).join(", ")}.`, "Finished results stay.");
|
|
583
|
+
}
|
|
584
|
+
else if (operation === "sweep_file") {
|
|
585
|
+
// Everything the person approves is on this screen: the shared rules, every item, the one success test, the
|
|
586
|
+
// limit. Confirming files exactly these and approves exactly these scopes; anything changed is a new card.
|
|
587
|
+
const sweepTitle = text(input, "title", 200).trim(), brief = text(input, "brief", 8000).trim();
|
|
588
|
+
const items = Array.isArray(input["items"]) ? input["items"] : [];
|
|
589
|
+
if (items.length < 2 || items.length > 20)
|
|
590
|
+
throw Error("A sweep is 2 to 20 items. For one, propose a task.");
|
|
591
|
+
const cleaned = items.map((one, index) => ({ title: text(one, "title", 200).trim(), item: text(one, "item", 4000).trim(), index }));
|
|
592
|
+
const maxParallel = integer(input, "maxParallel");
|
|
593
|
+
if (maxParallel < 1 || maxParallel > 8)
|
|
594
|
+
throw Error("Run 1 to 8 at once.");
|
|
595
|
+
request["title"] = sweepTitle;
|
|
596
|
+
request["brief"] = brief;
|
|
597
|
+
request["items"] = cleaned.map(({ title, item }) => ({ title, item }));
|
|
598
|
+
const project = repo.split(/[\\/]/).filter(Boolean).at(-1) ?? repo;
|
|
599
|
+
title = `Start a sweep in ${project}: ${sweepTitle}`;
|
|
600
|
+
// Every word each child's scope will hold is shown here (the security review: nothing approved off-screen): its
|
|
601
|
+
// goal is the shared rules, then its item, then the verdict line; its success test is the shared acceptance.
|
|
602
|
+
const acceptance = Array.isArray(input["acceptance"]) ? input["acceptance"] : [];
|
|
603
|
+
if (acceptance.length === 0)
|
|
604
|
+
throw Error("A sweep needs a success test every item shares.");
|
|
605
|
+
// Every child's scope exactly as filing would make it — its words AND its agent's permissions, limits and route —
|
|
606
|
+
// found by filing them in a savepoint that is always rolled back (prepare writes nothing). They are on this card and
|
|
607
|
+
// in its state: if a project default changes before confirm, the card is stale and nothing is filed or approved.
|
|
608
|
+
const children = sweepChildScopes(store, who, repo, brief, cleaned.map(({ title, item }) => ({ title, item })), input["acceptance"], now);
|
|
609
|
+
state = { children: children.map(one => one.fingerprint) };
|
|
610
|
+
const tests = acceptance.map((one, index) => `Success test ${index + 1}${typeof one["id"] === "string" ? ` (${one["id"]})` : ""}: ${String(one["statement"] ?? "")}${Array.isArray(one["evidence"]) ? ` — shown by ${one["evidence"].map(String).join(", ")}` : ""}`);
|
|
611
|
+
terms.push(`${cleaned.length} tasks, each in its own worktree on its own branch, at most ${maxParallel} running at once, built directly (no separate planning step).`, `Shared rules for every item: ${brief}`, ...cleaned.map(one => `Item ${one.index + 1} — ${one.title}: ${one.item}`), `Each item's goal is the shared rules, then its own item, then: ${VERDICT_RULE}`, ...tests.map(line => `Every item — ${line}`), ...(children.every(one => one.runtime === children[0].runtime) ? [`Every item — ${children[0].runtime}`] : children.map((one, index) => `Item ${index + 1} — ${one.runtime}`)), "Confirming files all of them and approves each one's scope exactly as shown. An item that needs a second person, or that the project's rules keep from its requester, is filed and waits for its own approval.", "Each ends with a verdict (fixed, not reproducible, blocked or done) and the sweep's table shows them as they land.");
|
|
612
|
+
}
|
|
557
613
|
else if (operation.startsWith("flow_")) {
|
|
558
614
|
const project = repo.split(/[\\/]/).filter(Boolean).at(-1) ?? repo;
|
|
559
615
|
if (operation === "flow_create") {
|
|
@@ -985,10 +1041,28 @@ function savedActionContext(store, who, id, state) {
|
|
|
985
1041
|
requireActor(store, who, payload.repo, payload.operation);
|
|
986
1042
|
return payload;
|
|
987
1043
|
}
|
|
1044
|
+
/** An action's terms alone, without whose proposal it was (actor, generation, ceiling). */
|
|
1045
|
+
const termsStampOf = (action) => hash({ operation: action.operation, request: action.request, repo: action.repo, state: action.state, terms: action.terms });
|
|
1046
|
+
/**
|
|
1047
|
+
* Whether the live action is the saved one: the exact stamp; or, for a scope approval already holding another
|
|
1048
|
+
* approver's yes on these exact terms (awaiting the second), the same terms — each reviewer's own nonce and authority
|
|
1049
|
+
* are still bound to them individually.
|
|
1050
|
+
*/
|
|
1051
|
+
function sameSharedAction(store, who, live, saved) {
|
|
1052
|
+
if (live.stamp === saved.stamp)
|
|
1053
|
+
return true;
|
|
1054
|
+
if (saved.operation !== "scope_approve" || termsStampOf(live) !== termsStampOf(saved))
|
|
1055
|
+
return false;
|
|
1056
|
+
const task = typeof saved.request["task"] === "string" ? saved.request["task"] : null;
|
|
1057
|
+
const scope = task === null ? null : store.getScope(task);
|
|
1058
|
+
if (scope === null)
|
|
1059
|
+
return false;
|
|
1060
|
+
return store.handle.prepare("SELECT 1 FROM scope_approval_vote WHERE task_id = ? AND digest = ? AND approver <> ? LIMIT 1").get(task, scope.digest, who.name) !== undefined;
|
|
1061
|
+
}
|
|
988
1062
|
export function mintSharedActionReview(store, who, id, root, now) {
|
|
989
1063
|
const payload = savedActionContext(store, who, id, "pending");
|
|
990
1064
|
const live = prepareSharedAction(store, who, payload.operation, payload.request, root, now);
|
|
991
|
-
if (live
|
|
1065
|
+
if (!sameSharedAction(store, who, live, payload))
|
|
992
1066
|
throw Error("This action changed. Ask for a fresh proposal.");
|
|
993
1067
|
const nonce = randomBytes(24).toString("hex");
|
|
994
1068
|
if (!store.mintCeremonyNonce({
|
|
@@ -1014,7 +1088,7 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1014
1088
|
if (hash(saved) !== hash(payload))
|
|
1015
1089
|
return refuse("refused", "This action does not match the saved proposal.");
|
|
1016
1090
|
live = prepareSharedAction(store, who, saved.operation, saved.request, options.root, now);
|
|
1017
|
-
if (live
|
|
1091
|
+
if (!sameSharedAction(store, who, live, saved))
|
|
1018
1092
|
return refuse("stale", "This action changed. Ask for a fresh proposal.");
|
|
1019
1093
|
}
|
|
1020
1094
|
catch (error) {
|
|
@@ -1022,6 +1096,8 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1022
1096
|
? error.message
|
|
1023
1097
|
: "This action is no longer available.");
|
|
1024
1098
|
}
|
|
1099
|
+
// The review nonce was minted against the saved proposal's stamp: it is checked against that, whoever reviews.
|
|
1100
|
+
const savedStamp = payload.stamp;
|
|
1025
1101
|
payload = live;
|
|
1026
1102
|
const config = CHAT_ACTIONS[live.operation], req = live.request;
|
|
1027
1103
|
const challenged = options.via !== "web" &&
|
|
@@ -1031,14 +1107,21 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1031
1107
|
if (sharedActionNeedsReview(payload) && !challenged) {
|
|
1032
1108
|
if (options.via !== "web" || !options.review || options.confirm !== true)
|
|
1033
1109
|
return refuse("needs-confirm", "Review the complete action in the secure confirmation screen.");
|
|
1034
|
-
if (!store.consumeCeremonyNonce(nonceHash(options.review.nonce), who.name, "chat-action", id,
|
|
1110
|
+
if (!store.consumeCeremonyNonce(nonceHash(options.review.nonce), who.name, "chat-action", id, savedStamp, now))
|
|
1035
1111
|
return refuse("needs-confirm", "This confirmation expired or was already used. Open the review again.");
|
|
1112
|
+
// A scope approval proves its person inside approve() (one transaction with the seal, under the same freshness
|
|
1113
|
+
// rule as the task page); here an empty field is only looked at, never spent (an explicit re-check is one-use).
|
|
1114
|
+
const emptyApproval = payload.operation === "scope_approve" && options.review.password === "";
|
|
1036
1115
|
if (config.password &&
|
|
1037
|
-
|
|
1116
|
+
(emptyApproval
|
|
1117
|
+
? !passwordlessSessionFor(store, who.name) && !ssoNowAvailable(who.name) && withFreshness("approve-standard", () => freshnessFor(who.name)) === null
|
|
1118
|
+
: !authenticateApprover(store, who.name, options.review.password, payload.repo).ok))
|
|
1038
1119
|
return refuse("needs-confirm", "Confirm with your password in the secure screen.");
|
|
1039
1120
|
}
|
|
1040
1121
|
try {
|
|
1041
|
-
|
|
1122
|
+
// A scope approval's re-check (sso-now) is spent only if it happens: a refusal or a rolled-back savepoint puts it
|
|
1123
|
+
// back (its every effect is in this transaction). Other actions spend it whatever comes of them.
|
|
1124
|
+
const act = () => store.savepoint(() => {
|
|
1042
1125
|
const task = typeof req["task"] === "string" ? req["task"] : null, repo = payload.repo, actor = who.name;
|
|
1043
1126
|
let taskId = task;
|
|
1044
1127
|
if (payload.operation === "lead_about_you") {
|
|
@@ -1076,6 +1159,14 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1076
1159
|
const done = runFlowAction(store, payload, actor, who.repos, options.root, now);
|
|
1077
1160
|
return { ok: true, taskId: null, said: done.said, href: done.href };
|
|
1078
1161
|
}
|
|
1162
|
+
else if (payload.operation === "sweep_cancel_rest") {
|
|
1163
|
+
const said = cancelSweepRest(store, who.name, payload.state["open"], now, "web");
|
|
1164
|
+
return { ok: true, taskId: null, said, href: `/sweeps/${Number(req["sweep"])}` };
|
|
1165
|
+
}
|
|
1166
|
+
else if (payload.operation === "sweep_file") {
|
|
1167
|
+
const done = fileSweep(store, who, id, payload, options.review?.password ?? "", now);
|
|
1168
|
+
return { ok: true, taskId: null, said: done.said, href: done.href };
|
|
1169
|
+
}
|
|
1079
1170
|
else if (payload.operation === "kit_setup") {
|
|
1080
1171
|
const kit = kitOf(String(req["kit"]));
|
|
1081
1172
|
const made = setUpKitNow(store, kit, repo, actor, now, null, true);
|
|
@@ -1117,11 +1208,14 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1117
1208
|
}, now);
|
|
1118
1209
|
else if (payload.operation === "scope_approve") {
|
|
1119
1210
|
const scope = store.getScope(task);
|
|
1120
|
-
|
|
1211
|
+
// Cockpit B2: classified by the act, not the road — the same standard-approval freshness as /t/:id/approve.
|
|
1212
|
+
const result = withFreshness("approve-standard", () => approve(store, task, actor, now, scope.digest, options.review.password));
|
|
1121
1213
|
if (!result.ok) {
|
|
1122
|
-
//
|
|
1214
|
+
// A first yes on protected work is kept, with how its person was proved: committed, never rolled back.
|
|
1215
|
+
// The card stays open (`awaiting`) for the second approver; the same person again changes nothing.
|
|
1123
1216
|
if (result.reason === "second-approver")
|
|
1124
|
-
|
|
1217
|
+
return { ok: true, taskId, awaiting: true, ...(result.already ? { already: true } : {}), said: gateWords({ verdict: "vote", have: result.have, need: 2, already: result.already }) };
|
|
1218
|
+
// v102: the project's approval rules speak for themselves.
|
|
1125
1219
|
if (result.reason === "requester" || result.reason === "person-required")
|
|
1126
1220
|
throw Error(gateWords({ verdict: "refuse", reason: result.reason }));
|
|
1127
1221
|
if (result.reason === "policy")
|
|
@@ -1187,6 +1281,7 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1187
1281
|
: "Project knowledge saved.";
|
|
1188
1282
|
return { ok: true, taskId, said };
|
|
1189
1283
|
});
|
|
1284
|
+
return payload.operation === "scope_approve" ? spendingOnlyOnSuccess(act, result => !result.ok || ("already" in result && result.already === true)) : act();
|
|
1190
1285
|
}
|
|
1191
1286
|
catch (error) {
|
|
1192
1287
|
return refuse("refused", error instanceof Error
|
|
@@ -1195,6 +1290,93 @@ export function executeSharedAction(store, who, id, payload, now, options) {
|
|
|
1195
1290
|
}
|
|
1196
1291
|
}
|
|
1197
1292
|
/** A confirmed subagent action (v92), through the same helpers the Subagents pages use. */
|
|
1293
|
+
/**
|
|
1294
|
+
* Lead fan-out: file every item (all of them or none — any refusal throws and the savepoint takes the whole sweep
|
|
1295
|
+
* back), link them to one sweep record, then approve each one's scope through the ordinary approval (its own digest,
|
|
1296
|
+
* its own rules). A child the approval refuses stays filed and unapproved; it is named, never retried.
|
|
1297
|
+
*/
|
|
1298
|
+
/** One child as filing makes it (the shared brief, its item, the verdict rule; the shared acceptance; built directly). */
|
|
1299
|
+
function fileSweepChild(store, who, repo, brief, one, acceptance, now) {
|
|
1300
|
+
const goal = `${brief}\n\nThis item:\n${one.item}\n\n${VERDICT_RULE}`;
|
|
1301
|
+
return fileTaskProposal(store, {
|
|
1302
|
+
title: one.title, repo, goal, acceptance, planning: "skip",
|
|
1303
|
+
// Sized now, by the same heuristic filing would use: no background classifier is started — the card's dry run must
|
|
1304
|
+
// leave nothing running after its rollback, and the approved scope must be the one shown (Codex sweep r2).
|
|
1305
|
+
sizing: heuristicSizing({ title: one.title, goal, outOfScope: null }),
|
|
1306
|
+
filedVia: "mate", filedBy: { name: who.name, kind: "person" }, proposedVia: "mate", admittedRepos: [...who.repos],
|
|
1307
|
+
}, now);
|
|
1308
|
+
}
|
|
1309
|
+
/** A filed child's scope in the words an approval agrees to (no identity lines), its runtime terms, and their digest. */
|
|
1310
|
+
function sweepChildScopeOf(store, taskId) {
|
|
1311
|
+
const scope = store.getScope(taskId);
|
|
1312
|
+
if (scope === null)
|
|
1313
|
+
throw Error("A sweep item has no scope to show.");
|
|
1314
|
+
const authority = scopeAuthorityOf(scope, { authMode: readAuthModeStrict });
|
|
1315
|
+
if (!authority.ok)
|
|
1316
|
+
throw Error(authority.problem);
|
|
1317
|
+
const words = describeScope(scope).filter(line => !/^\s*(reference|approved)\s/.test(line)).map(line => line.trim());
|
|
1318
|
+
const runtime = `Agent permissions and limits: ${runtimeTerms(authority.profile)} ${authority.authMode === "api-key" ? "Uses your API key; spend is charged to that account." : authority.authMode === "subscription" ? "Uses your subscription login." : ""}`.trim();
|
|
1319
|
+
// The fingerprint covers the scope's every field, not only what is rendered (a profile setting no sentence names, such
|
|
1320
|
+
// as an agent's idle timeout, still makes the card stale): its exact terms with only identity left out.
|
|
1321
|
+
const { taskId: _task, digest: _digest, approvedDigest: _approved, approvedBy: _by, approvedAt: _at, proposedAt: _proposed, approvalBasis: _basis, modeDigest: _mode, approvedProfile: _approvedProfile, approvedProfileJson: _approvedProfileJson, approvedChainJson: _approvedChain, ...terms } = scope;
|
|
1322
|
+
return { fingerprint: hash({ words, runtime, authMode: authority.authMode, profile: authority.profile, terms }), runtime, words };
|
|
1323
|
+
}
|
|
1324
|
+
/** Every child as filing would make it, found in a savepoint that is always rolled back: prepare never writes. */
|
|
1325
|
+
function sweepChildScopes(store, who, repo, brief, items, acceptance, now) {
|
|
1326
|
+
const dryRun = new Error("sweep dry run");
|
|
1327
|
+
let found = [];
|
|
1328
|
+
// Inside one transaction (filing opens its own; transact flattens), a savepoint that always rolls back.
|
|
1329
|
+
store.transact(() => {
|
|
1330
|
+
try {
|
|
1331
|
+
store.savepoint(() => {
|
|
1332
|
+
found = items.map((one, index) => {
|
|
1333
|
+
const made = fileSweepChild(store, who, repo, brief, one, acceptance, now);
|
|
1334
|
+
if (!made.ok)
|
|
1335
|
+
throw Error(`Item ${index + 1} (${one.title}) can't be filed: ${made.message}`);
|
|
1336
|
+
return sweepChildScopeOf(store, made.id);
|
|
1337
|
+
});
|
|
1338
|
+
throw dryRun;
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
catch (error) {
|
|
1342
|
+
if (error !== dryRun)
|
|
1343
|
+
throw error;
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
return found;
|
|
1347
|
+
}
|
|
1348
|
+
function fileSweep(store, who, proposal, payload, password, now) {
|
|
1349
|
+
const req = payload.request;
|
|
1350
|
+
const already = store.sweepOfProposal(proposal);
|
|
1351
|
+
if (already !== null)
|
|
1352
|
+
return { said: "This sweep was already started.", href: `/sweeps/${already}` };
|
|
1353
|
+
const items = req["items"];
|
|
1354
|
+
const brief = String(req["brief"]);
|
|
1355
|
+
const sweep = store.createSweep({ owner: who.name, thread: store.getLeadProposal(proposal)?.thread ?? null, repo: payload.repo, title: String(req["title"]), brief,
|
|
1356
|
+
maxParallel: Number(req["maxParallel"]), proposal }, now);
|
|
1357
|
+
const filed = [];
|
|
1358
|
+
for (const [index, one] of items.entries()) {
|
|
1359
|
+
const made = fileSweepChild(store, who, payload.repo, brief, one, req["acceptance"], now);
|
|
1360
|
+
if (!made.ok)
|
|
1361
|
+
throw Error(`Item ${index + 1} (${one.title}) couldn't be filed: ${made.message}. Nothing was filed.`);
|
|
1362
|
+
// Each filed scope must be exactly the one the card showed (words, permissions, limits, route); else nothing is filed.
|
|
1363
|
+
if (sweepChildScopeOf(store, made.id).fingerprint !== payload.state["children"]?.[index])
|
|
1364
|
+
throw Error(`Item ${index + 1} (${one.title}) no longer matches what you reviewed (a project default changed). Nothing was filed; ask for a fresh card.`);
|
|
1365
|
+
store.addSweepTask(sweep, made.id, index, one.title);
|
|
1366
|
+
filed.push({ id: made.id, title: one.title });
|
|
1367
|
+
}
|
|
1368
|
+
const waiting = [];
|
|
1369
|
+
for (const one of filed) {
|
|
1370
|
+
const scope = store.getScope(one.id);
|
|
1371
|
+
const approved = scope === null ? null : withFreshness("approve-standard", () => approve(store, one.id, who.name, now, scope.digest, password));
|
|
1372
|
+
if (approved === null || !approved.ok)
|
|
1373
|
+
waiting.push(one.title);
|
|
1374
|
+
}
|
|
1375
|
+
const said = waiting.length === 0
|
|
1376
|
+
? `Started: ${filed.length} tasks, at most ${Number(req["maxParallel"])} at once.`
|
|
1377
|
+
: `Started: ${filed.length - waiting.length} of ${filed.length} approved; ${waiting.length} wait for their own approval (${waiting.join(", ")}).`;
|
|
1378
|
+
return { said, href: `/sweeps/${sweep}` };
|
|
1379
|
+
}
|
|
1198
1380
|
function runSubagentAction(store, payload, actor, now) {
|
|
1199
1381
|
const req = payload.request;
|
|
1200
1382
|
if (payload.operation === "subagent_create") {
|
|
@@ -1331,9 +1513,9 @@ function runFlowAction(store, payload, actor, repos, root, now) {
|
|
|
1331
1513
|
if (payload.operation.startsWith("flow_trigger_")) {
|
|
1332
1514
|
const trigger = store.getFlowTrigger(Number(req["trigger"]));
|
|
1333
1515
|
if (payload.operation === "flow_trigger_remove")
|
|
1334
|
-
removeFlowTrigger(store, trigger, now, configDirOf(store));
|
|
1516
|
+
removeFlowTrigger(store, trigger, now, configDirOf(store), actor);
|
|
1335
1517
|
else
|
|
1336
|
-
store.updateFlowTrigger(trigger.id, { state: payload.operation === "flow_trigger_pause" ? "paused" : "active" }, now);
|
|
1518
|
+
store.updateFlowTrigger(trigger.id, { state: payload.operation === "flow_trigger_pause" ? "paused" : "active" }, now, actor);
|
|
1337
1519
|
return { said: payload.operation === "flow_trigger_remove" ? "Trigger removed." : payload.operation === "flow_trigger_pause" ? "Trigger paused." : "Trigger on again.", href: `/flows/${trigger.flow}` };
|
|
1338
1520
|
}
|
|
1339
1521
|
const card = store.getFlowCard(Number(req["card"]));
|
package/dist/chat-ask.d.ts
CHANGED
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { ChatBinding, ChatEvent, ChatState } from "./chat-delivery-state.js";
|
|
14
14
|
import { type LeadAsk, type Store } from "./store.js";
|
|
15
|
+
/** Whether a task (the execution shown, never only its family's root) moved past a lead's question asked at `since`:
|
|
16
|
+
* its plan was rewritten or approved, or a new attempt of it started. The options answered a state that is gone. A run
|
|
17
|
+
* that merely finishes, or a review beside it, answers nothing, so it never takes the question's buttons. */
|
|
18
|
+
export declare function taskMovedSince(store: Store, taskId: string, since: string): boolean;
|
|
15
19
|
/** The question as text with its options, where no buttons are drawn. */
|
|
16
20
|
export declare function chatAskText(ask: Pick<LeadAsk, "question" | "options">): string;
|
|
17
21
|
/** The live buttons on one part, in the order they were minted: the options, then "Something else". None when the part
|
package/dist/chat-ask.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { chatHash, partContent, savedChatPart } from "./chat-delivery-state.js";
|
|
2
2
|
import { LEAD_ASK_OTHER } from "./store.js";
|
|
3
|
+
/** Whether a task (the execution shown, never only its family's root) moved past a lead's question asked at `since`:
|
|
4
|
+
* its plan was rewritten or approved, or a new attempt of it started. The options answered a state that is gone. A run
|
|
5
|
+
* that merely finishes, or a review beside it, answers nothing, so it never takes the question's buttons. */
|
|
6
|
+
export function taskMovedSince(store, taskId, since) {
|
|
7
|
+
const at = new Date(since).getTime();
|
|
8
|
+
if (Number.isNaN(at))
|
|
9
|
+
return false;
|
|
10
|
+
const after = (iso) => iso != null && new Date(iso).getTime() > at;
|
|
11
|
+
const scope = store.getScope(taskId);
|
|
12
|
+
if (scope !== null && (after(scope.proposedAt) || after(scope.approvedAt)))
|
|
13
|
+
return true;
|
|
14
|
+
const ref = store.lookupRef(taskId);
|
|
15
|
+
return ref !== null && store.runsFor(ref.id).some(run => run.role !== "reviewer" && after(run.startedAt));
|
|
16
|
+
}
|
|
3
17
|
/** The question as text with its options, where no buttons are drawn. */
|
|
4
18
|
export function chatAskText(ask) {
|
|
5
19
|
return [ask.question, ...ask.options.map(one => `• ${one}`), `• ${LEAD_ASK_OTHER}: say it in your own words`].join("\n");
|
package/dist/chat-channel.js
CHANGED
|
@@ -509,6 +509,11 @@ export const CHAT_ACTION_PARITY = {
|
|
|
509
509
|
how: "Read by the model during a phone turn over the enrolled ceiling.",
|
|
510
510
|
gap: null,
|
|
511
511
|
},
|
|
512
|
+
find_tools: {
|
|
513
|
+
support: "direct",
|
|
514
|
+
how: "Searches the lead's own other tools by what it wants to do; what it finds is offered to it from its next step in the same reply. Same on every channel: the shared engine runs it.",
|
|
515
|
+
gap: null,
|
|
516
|
+
},
|
|
512
517
|
list_repos: {
|
|
513
518
|
support: "direct",
|
|
514
519
|
how: "Read during a turn; projects are r1..rN in enrollment order, as on the console.",
|
|
@@ -583,6 +588,21 @@ export const CHAT_ACTION_PARITY = {
|
|
|
583
588
|
how: "Card with Confirm/Dismiss through confirmLeadProposal (filed as a lead proposal, via telegram). The confirmed card links Review & start for the filed task while its scope waits; under a signed automatic mode it says the scope is approved and links the task.",
|
|
584
589
|
gap: "Under manual approval the password step happens in the console, reached from the card's button.",
|
|
585
590
|
},
|
|
591
|
+
propose_sweep: {
|
|
592
|
+
support: "direct",
|
|
593
|
+
how: "A protected shared action (sweep_file): the card lists every item, the shared rules and the success test; confirming files them all and approves each scope.",
|
|
594
|
+
gap: "Starting a sweep approves work, so it opens the secure review in the console (the password, or Passwordless).",
|
|
595
|
+
},
|
|
596
|
+
get_sweep: {
|
|
597
|
+
support: "direct",
|
|
598
|
+
how: "Reads a sweep's table (each item's state, verdict and result) from the local database through the shared engine.",
|
|
599
|
+
gap: null,
|
|
600
|
+
},
|
|
601
|
+
propose_sweep_cancel: {
|
|
602
|
+
support: "direct",
|
|
603
|
+
how: "An ordinary shared action (sweep_cancel_rest): Confirm cancels every item not yet ready; finished results stay.",
|
|
604
|
+
gap: null,
|
|
605
|
+
},
|
|
586
606
|
propose_scope: {
|
|
587
607
|
support: "direct",
|
|
588
608
|
how: "Confirm rewrites the scope through the shared door; the confirmed card links Review & start for the exact task.",
|
package/dist/chat-continuity.js
CHANGED
|
@@ -63,7 +63,7 @@ export const CHAT_CONTINUITY_SCRIPT = String.raw `
|
|
|
63
63
|
var carry=JSON.parse(sessionStorage.getItem(carryKey)||'null');sessionStorage.removeItem(carryKey);
|
|
64
64
|
if(!draft&&carry&&user&&carry.owner===user&&carry.task===task&&typeof carry.text==='string'&&carry.text.length<=2000&&typeof carry.at==='number'&&carry.at>Date.now()-86400000){box.value=carry.text;draft={text:carry.text,request:request.value,submitted:false,at:Date.now()};save();}
|
|
65
65
|
}catch(e){storage=false;}
|
|
66
|
-
function buttons(){document.querySelectorAll('form[action="/chat"] button[type="submit"]').forEach(function(button){button.disabled=
|
|
66
|
+
function buttons(){document.querySelectorAll('form[action="/chat"] button[type="submit"]').forEach(function(button){button.disabled=sending||stale||offline;});}
|
|
67
67
|
function showReconnect(on){if(reconnect)reconnect.hidden=!on;}
|
|
68
68
|
function lostAuthority(){stale=true;buttons();showReconnect(true);say('Sign in again to reconnect. Your draft stays in this tab.');}
|
|
69
69
|
function changed(){stale=true;buttons();showReconnect(true);say('This conversation changed or ended. Your draft stays in this tab; reconnect to continue.');}
|
|
@@ -97,7 +97,8 @@ export const CHAT_CONTINUITY_SCRIPT = String.raw `
|
|
|
97
97
|
if(!(one instanceof HTMLFormElement))return;
|
|
98
98
|
var action=one.getAttribute('action')||'';
|
|
99
99
|
if(action==='/chat'){
|
|
100
|
-
|
|
100
|
+
// A reply in progress no longer holds the composer: a message sent now waits its turn (the lead queue).
|
|
101
|
+
if(sending||stale||offline){event.preventDefault();return;}
|
|
101
102
|
bind(one);
|
|
102
103
|
if(one===form){
|
|
103
104
|
if(!box.value.trim()){event.preventDefault();return;}
|
|
@@ -125,7 +126,7 @@ export const CHAT_CONTINUITY_SCRIPT = String.raw `
|
|
|
125
126
|
var data=got.data||{};
|
|
126
127
|
if(data.ok===true){
|
|
127
128
|
if(String(data.session)!==session){changed();return;}
|
|
128
|
-
say('Sent. Waiting for the reply…');later(300);return;
|
|
129
|
+
say(data.queued===true?'Queued. It goes to the lead after the reply in progress.':'Sent. Waiting for the reply…');later(300);return;
|
|
129
130
|
}
|
|
130
131
|
// Refused, so nothing was received: the draft stays, editable and
|
|
131
132
|
// resendable under its own key; the words are the server's.
|
|
@@ -302,7 +303,7 @@ export const CHAT_CONTINUITY_SCRIPT = String.raw `
|
|
|
302
303
|
}
|
|
303
304
|
settleLive();
|
|
304
305
|
busy=data.pending;buttons();
|
|
305
|
-
say(busy?'Reply in progress.
|
|
306
|
+
say(busy?'Reply in progress. A message you send now waits its turn.':sent?'Message not confirmed. Check the conversation before retrying.':'');
|
|
306
307
|
later(busy?2500:5000);
|
|
307
308
|
}catch(e){say('Connection lost. Reconnecting… Your existing work is not cancelled.');later(10000);}
|
|
308
309
|
finally{polling=false;}
|
package/dist/chat-decide.d.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/** Decisions finish in the chat app. A pushed card about a result, a failed task, a plan waiting for approval or a
|
|
2
|
-
* pull request ready to merge carries buttons that act in place: Accept and finish, Request changes, Retry,
|
|
3
|
-
* Approve & start, Merge. Every act that changes something asks twice ("Accept and finish 'X'? [Yes] [Cancel]"),
|
|
4
|
-
* and each button is one opaque token bound to the exact result, receipt, plan or commit the card showed: a card a
|
|
5
|
-
* newer result, a finished task or a changed plan has overtaken says so and acts on nothing.
|
|
6
|
-
*
|
|
7
|
-
* Approving a plan or merging needs the password, which never goes in chat, so they act in chat only where the owner
|
|
8
|
-
* turned approving from chat on (their lasting setting, chat-approval.ts, or a signed mode's chatApprove term), for
|
|
9
|
-
* their own chat; otherwise, and for a plan that widens permissions, exceeds the limit or touches protected paths, the
|
|
10
|
-
* card keeps its link.
|
|
11
|
-
*
|
|
12
|
-
* The same in every chat app: Telegram's bridge and the shared engine Slack, Discord and Teams run on (chat-delivery.ts)
|
|
13
|
-
* offer, mint and answer these through the same tokens (the `channel` column). Every act runs through the door the
|
|
14
|
-
* console uses. */
|
|
15
1
|
import { DECIDE_ACTS } from "./contracts/chat-callback-rows.js";
|
|
16
2
|
import { type ChatApprovalLimits } from "./chat-approval.js";
|
|
17
3
|
import { type VerifiedApprover } from "./principal.js";
|
package/dist/chat-decide.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* The same in every chat app: Telegram's bridge and the shared engine Slack, Discord and Teams run on (chat-delivery.ts)
|
|
13
13
|
* offer, mint and answer these through the same tokens (the `channel` column). Every act runs through the door the
|
|
14
14
|
* console uses. */
|
|
15
|
+
import { isAcpProfile } from "./acp-agents.js";
|
|
15
16
|
import { DECIDE_ACTS, readDecideActionRow } from "./contracts/chat-callback-rows.js";
|
|
16
17
|
import { createHash, randomBytes } from "node:crypto";
|
|
17
18
|
import { acceptAndCompleteAsOperator, assignmentOf, owedAcceptance } from "./assignment.js";
|
|
@@ -81,13 +82,15 @@ export function permissionPlainWords(profile) {
|
|
|
81
82
|
}
|
|
82
83
|
if (profile.provider === "gemini")
|
|
83
84
|
return profile.approvalArgv === "yolo" ? full : "file edits only; other tools are refused";
|
|
85
|
+
if (isAcpProfile(profile))
|
|
86
|
+
return "full access inside the OS fence, nothing asks first";
|
|
84
87
|
return profile.sandboxMode === "danger-full-access" ? full : "file edits and commands inside the project only";
|
|
85
88
|
}
|
|
86
89
|
function fullAccess(profile) {
|
|
87
90
|
if (profile === null || profile === undefined)
|
|
88
91
|
return false;
|
|
89
92
|
return profile.provider === "claude" ? profile.permissionArgv === "bypassPermissions"
|
|
90
|
-
: profile.provider === "gemini" ? profile.approvalArgv === "yolo" : profile.sandboxMode === "danger-full-access";
|
|
93
|
+
: profile.provider === "gemini" ? profile.approvalArgv === "yolo" : isAcpProfile(profile) ? true : profile.sandboxMode === "danger-full-access";
|
|
91
94
|
}
|
|
92
95
|
const money = (micro) => `$${(micro / 1_000_000).toFixed(2)}`;
|
|
93
96
|
/** A plan this person's chat may approve now: the term, the plan's exact bytes, and none of the reasons it must open
|
package/dist/chat-delivery.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import { ChatState, type ChatBinding, type ChatContent, type ChatEvent, type Cha
|
|
|
3
3
|
import { type Store } from "./store.js";
|
|
4
4
|
import type { SubscriptionLeadRunner } from "./subscription-chat.js";
|
|
5
5
|
import { type ChatMerge } from "./chat-decide.js";
|
|
6
|
+
/**
|
|
7
|
+
* The watch at a chat app's final fence (cockpit §3, R11), right before the network: a notice about an agent its
|
|
8
|
+
* person has open now waits, as a held part, until the watch ends; then it is delivered as usual. The subject is the
|
|
9
|
+
* part's own run, or its task's live build. Only a part planned with `watch` (never a security alert or the digest)
|
|
10
|
+
* can be held. True: held, so nothing is sent now.
|
|
11
|
+
*/
|
|
12
|
+
export declare function heldForWatch(state: ChatState, store: Store, part: number, event: {
|
|
13
|
+
kind: string;
|
|
14
|
+
}, approver: string, content: ChatContent, now: Date): boolean;
|
|
6
15
|
export declare const chatObject: (v: unknown) => Record<string, unknown>;
|
|
7
16
|
export type ChatDeliveryOptions = {
|
|
8
17
|
store: Store;
|