toolroll 0.5.0
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/LICENSE +21 -0
- package/README.md +1113 -0
- package/THIRD_PARTY_NOTICES.md +449 -0
- package/dist/accent-colors.d.ts +48 -0
- package/dist/accent-colors.js +122 -0
- package/dist/action-ledger.d.ts +26 -0
- package/dist/action-ledger.js +75 -0
- package/dist/agent-fence.d.ts +42 -0
- package/dist/agent-fence.js +183 -0
- package/dist/agentconfig.d.ts +216 -0
- package/dist/agentconfig.js +454 -0
- package/dist/api-tokens.d.ts +16 -0
- package/dist/api-tokens.js +27 -0
- package/dist/approval-policy.d.ts +69 -0
- package/dist/approval-policy.js +137 -0
- package/dist/approval-rules-ui.d.ts +25 -0
- package/dist/approval-rules-ui.js +39 -0
- package/dist/assignment-adapters.d.ts +180 -0
- package/dist/assignment-adapters.js +239 -0
- package/dist/assignment-brief.d.ts +71 -0
- package/dist/assignment-brief.js +129 -0
- package/dist/assignment-delivery.d.ts +56 -0
- package/dist/assignment-delivery.js +160 -0
- package/dist/assignment-presentation.d.ts +26 -0
- package/dist/assignment-presentation.js +80 -0
- package/dist/assignment-status.d.ts +62 -0
- package/dist/assignment-status.js +152 -0
- package/dist/assignment-ui.d.ts +71 -0
- package/dist/assignment-ui.js +103 -0
- package/dist/assignment.d.ts +222 -0
- package/dist/assignment.js +399 -0
- package/dist/attest.d.ts +56 -0
- package/dist/attest.js +153 -0
- package/dist/backend.d.ts +97 -0
- package/dist/backend.js +166 -0
- package/dist/backup-ui.d.ts +22 -0
- package/dist/backup-ui.js +59 -0
- package/dist/backup.d.ts +84 -0
- package/dist/backup.js +421 -0
- package/dist/beads.d.ts +34 -0
- package/dist/beads.js +135 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +22 -0
- package/dist/board.d.ts +150 -0
- package/dist/board.js +210 -0
- package/dist/boot-identity.d.ts +63 -0
- package/dist/boot-identity.js +99 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
- package/dist/browser/workspace.css +4 -0
- package/dist/browser/workspace.js +225 -0
- package/dist/browser-crew.d.ts +11 -0
- package/dist/browser-crew.js +50 -0
- package/dist/browser-shell.d.ts +8 -0
- package/dist/browser-shell.js +60 -0
- package/dist/browser-workspace.d.ts +780 -0
- package/dist/browser-workspace.js +47 -0
- package/dist/budget-alerts.d.ts +19 -0
- package/dist/budget-alerts.js +46 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.js +3262 -0
- package/dist/capscan.d.ts +35 -0
- package/dist/capscan.js +113 -0
- package/dist/chat-acceptance.d.ts +71 -0
- package/dist/chat-acceptance.js +120 -0
- package/dist/chat-actions.d.ts +265 -0
- package/dist/chat-actions.js +1273 -0
- package/dist/chat-channel.d.ts +109 -0
- package/dist/chat-channel.js +652 -0
- package/dist/chat-continuity.d.ts +32 -0
- package/dist/chat-continuity.js +317 -0
- package/dist/chat-controls.d.ts +113 -0
- package/dist/chat-controls.js +53 -0
- package/dist/chat-delivery-state.d.ts +159 -0
- package/dist/chat-delivery-state.js +309 -0
- package/dist/chat-delivery.d.ts +38 -0
- package/dist/chat-delivery.js +693 -0
- package/dist/chat-display.d.ts +1 -0
- package/dist/chat-display.js +18 -0
- package/dist/chat-evidence.d.ts +123 -0
- package/dist/chat-evidence.js +207 -0
- package/dist/chat-flow.d.ts +44 -0
- package/dist/chat-flow.js +215 -0
- package/dist/chat-inbox.d.ts +57 -0
- package/dist/chat-inbox.js +123 -0
- package/dist/chat-polish.d.ts +16 -0
- package/dist/chat-polish.js +135 -0
- package/dist/chat-review.d.ts +46 -0
- package/dist/chat-review.js +98 -0
- package/dist/chat-rooms.d.ts +123 -0
- package/dist/chat-rooms.js +216 -0
- package/dist/chat-task-actions.d.ts +44 -0
- package/dist/chat-task-actions.js +79 -0
- package/dist/check-progress.d.ts +51 -0
- package/dist/check-progress.js +290 -0
- package/dist/child-database.d.ts +12 -0
- package/dist/child-database.js +30 -0
- package/dist/claim.d.ts +688 -0
- package/dist/claim.js +1740 -0
- package/dist/cli.d.ts +137 -0
- package/dist/cli.js +1461 -0
- package/dist/codex-limits.d.ts +15 -0
- package/dist/codex-limits.js +145 -0
- package/dist/coding-context.d.ts +55 -0
- package/dist/coding-context.js +92 -0
- package/dist/coding-handoff.d.ts +57 -0
- package/dist/coding-handoff.js +277 -0
- package/dist/coding-provider.d.ts +72 -0
- package/dist/coding-provider.js +424 -0
- package/dist/coding-shipping-ui.d.ts +4 -0
- package/dist/coding-shipping-ui.js +12 -0
- package/dist/coding-types.d.ts +62 -0
- package/dist/coding-types.js +1 -0
- package/dist/coding-ui.d.ts +17 -0
- package/dist/coding-ui.js +364 -0
- package/dist/coding-update.d.ts +15 -0
- package/dist/coding-update.js +149 -0
- package/dist/coding-workspace.d.ts +124 -0
- package/dist/coding-workspace.js +768 -0
- package/dist/container-state.d.ts +5 -0
- package/dist/container-state.js +62 -0
- package/dist/containment.d.ts +197 -0
- package/dist/containment.js +559 -0
- package/dist/contest.d.ts +318 -0
- package/dist/contest.js +753 -0
- package/dist/control-setup.d.ts +35 -0
- package/dist/control-setup.js +40 -0
- package/dist/control-ui.d.ts +29 -0
- package/dist/control-ui.js +53 -0
- package/dist/controller-service.d.ts +1 -0
- package/dist/controller-service.js +19 -0
- package/dist/controller-supervisor.d.ts +30 -0
- package/dist/controller-supervisor.js +104 -0
- package/dist/converse.d.ts +304 -0
- package/dist/converse.js +849 -0
- package/dist/coordinator-proposals.d.ts +29 -0
- package/dist/coordinator-proposals.js +137 -0
- package/dist/coordinator.d.ts +186 -0
- package/dist/coordinator.js +447 -0
- package/dist/credentials-ui.d.ts +25 -0
- package/dist/credentials-ui.js +48 -0
- package/dist/daemon.d.ts +209 -0
- package/dist/daemon.js +604 -0
- package/dist/decision.d.ts +120 -0
- package/dist/decision.js +388 -0
- package/dist/demo.d.ts +55 -0
- package/dist/demo.js +992 -0
- package/dist/desktop-access.d.ts +28 -0
- package/dist/desktop-access.js +88 -0
- package/dist/desktop-bundle.d.ts +16 -0
- package/dist/desktop-bundle.js +74 -0
- package/dist/desktop-host.d.ts +54 -0
- package/dist/desktop-host.js +508 -0
- package/dist/desktop-update-gate.d.ts +12 -0
- package/dist/desktop-update-gate.js +91 -0
- package/dist/desktop-update-recovery.d.ts +14 -0
- package/dist/desktop-update-recovery.js +243 -0
- package/dist/desktop-update.d.ts +110 -0
- package/dist/desktop-update.js +740 -0
- package/dist/discord-api.d.ts +20 -0
- package/dist/discord-api.js +138 -0
- package/dist/discord-chat.d.ts +16 -0
- package/dist/discord-chat.js +381 -0
- package/dist/discord-settings.d.ts +7 -0
- package/dist/discord-settings.js +75 -0
- package/dist/discord.d.ts +10 -0
- package/dist/discord.js +169 -0
- package/dist/discover.d.ts +75 -0
- package/dist/discover.js +150 -0
- package/dist/dispatch.d.ts +100 -0
- package/dist/dispatch.js +311 -0
- package/dist/dispose.d.ts +161 -0
- package/dist/dispose.js +803 -0
- package/dist/email-settings.d.ts +34 -0
- package/dist/email-settings.js +50 -0
- package/dist/envelope.d.ts +47 -0
- package/dist/envelope.js +105 -0
- package/dist/evidence-pack.d.ts +185 -0
- package/dist/evidence-pack.js +258 -0
- package/dist/evidence.d.ts +345 -0
- package/dist/evidence.js +782 -0
- package/dist/exec.d.ts +285 -0
- package/dist/exec.js +1841 -0
- package/dist/exhaustion.d.ts +85 -0
- package/dist/exhaustion.js +141 -0
- package/dist/export-ui.d.ts +4 -0
- package/dist/export-ui.js +17 -0
- package/dist/export.d.ts +48 -0
- package/dist/export.js +305 -0
- package/dist/flow-actions.d.ts +77 -0
- package/dist/flow-actions.js +203 -0
- package/dist/flow-code.d.ts +50 -0
- package/dist/flow-code.js +159 -0
- package/dist/flow-draft.d.ts +38 -0
- package/dist/flow-draft.js +80 -0
- package/dist/flow-engine.d.ts +66 -0
- package/dist/flow-engine.js +416 -0
- package/dist/flow-insights.d.ts +95 -0
- package/dist/flow-insights.js +149 -0
- package/dist/flow-live.d.ts +24 -0
- package/dist/flow-live.js +150 -0
- package/dist/flow-people.d.ts +24 -0
- package/dist/flow-people.js +91 -0
- package/dist/flow-replies.d.ts +48 -0
- package/dist/flow-replies.js +113 -0
- package/dist/flow-scripts.d.ts +38 -0
- package/dist/flow-scripts.js +73 -0
- package/dist/flow-secrets.d.ts +13 -0
- package/dist/flow-secrets.js +45 -0
- package/dist/flow-sort.d.ts +82 -0
- package/dist/flow-sort.js +153 -0
- package/dist/flow-steps.d.ts +40 -0
- package/dist/flow-steps.js +408 -0
- package/dist/flow-triggers.d.ts +244 -0
- package/dist/flow-triggers.js +959 -0
- package/dist/flows-ui.d.ts +20 -0
- package/dist/flows-ui.js +175 -0
- package/dist/flows.d.ts +229 -0
- package/dist/flows.js +968 -0
- package/dist/fonts.d.ts +19 -0
- package/dist/fonts.js +19 -0
- package/dist/gaps.d.ts +35 -0
- package/dist/gaps.js +101 -0
- package/dist/gate-failure.d.ts +40 -0
- package/dist/gate-failure.js +66 -0
- package/dist/git.d.ts +54 -0
- package/dist/git.js +94 -0
- package/dist/google-mail.d.ts +58 -0
- package/dist/google-mail.js +151 -0
- package/dist/grant.d.ts +136 -0
- package/dist/grant.js +238 -0
- package/dist/graph.d.ts +164 -0
- package/dist/graph.js +383 -0
- package/dist/guides.d.ts +22 -0
- package/dist/guides.js +363 -0
- package/dist/held.d.ts +150 -0
- package/dist/held.js +799 -0
- package/dist/invoke.d.ts +112 -0
- package/dist/invoke.js +780 -0
- package/dist/issues.d.ts +41 -0
- package/dist/issues.js +131 -0
- package/dist/job-object-helper.ps1 +252 -0
- package/dist/jsonl-discriminants.d.ts +11 -0
- package/dist/jsonl-discriminants.js +93 -0
- package/dist/keys.d.ts +104 -0
- package/dist/keys.js +229 -0
- package/dist/kits-ui.d.ts +17 -0
- package/dist/kits-ui.js +46 -0
- package/dist/kits.d.ts +86 -0
- package/dist/kits.js +215 -0
- package/dist/knowledge-cli.d.ts +101 -0
- package/dist/knowledge-cli.js +92 -0
- package/dist/knowledge-ui.d.ts +24 -0
- package/dist/knowledge-ui.js +40 -0
- package/dist/lead-context.d.ts +8 -0
- package/dist/lead-context.js +56 -0
- package/dist/lead-follow.d.ts +22 -0
- package/dist/lead-follow.js +167 -0
- package/dist/lead-status.d.ts +75 -0
- package/dist/lead-status.js +285 -0
- package/dist/ledger-chain.d.ts +46 -0
- package/dist/ledger-chain.js +187 -0
- package/dist/ledger-csv.d.ts +4 -0
- package/dist/ledger-csv.js +11 -0
- package/dist/ledger-view.d.ts +24 -0
- package/dist/ledger-view.js +58 -0
- package/dist/limits-ui.d.ts +26 -0
- package/dist/limits-ui.js +67 -0
- package/dist/link.d.ts +72 -0
- package/dist/link.js +217 -0
- package/dist/live.d.ts +96 -0
- package/dist/live.js +366 -0
- package/dist/liveness.d.ts +30 -0
- package/dist/liveness.js +42 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +25 -0
- package/dist/mailbox.d.ts +57 -0
- package/dist/mailbox.js +150 -0
- package/dist/maintenance.d.ts +11 -0
- package/dist/maintenance.js +35 -0
- package/dist/mate-cli.d.ts +52 -0
- package/dist/mate-cli.js +345 -0
- package/dist/mate-contract.d.ts +10 -0
- package/dist/mate-contract.js +30 -0
- package/dist/mate-doors.d.ts +69 -0
- package/dist/mate-doors.js +548 -0
- package/dist/mate-progress.d.ts +29 -0
- package/dist/mate-progress.js +121 -0
- package/dist/mate-tools.d.ts +94 -0
- package/dist/mate-tools.js +1878 -0
- package/dist/mate.d.ts +92 -0
- package/dist/mate.js +435 -0
- package/dist/mcp-connect.d.ts +103 -0
- package/dist/mcp-connect.js +252 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +651 -0
- package/dist/memory-cli.d.ts +466 -0
- package/dist/memory-cli.js +111 -0
- package/dist/memory-pass.d.ts +160 -0
- package/dist/memory-pass.js +409 -0
- package/dist/metrics.d.ts +3 -0
- package/dist/metrics.js +56 -0
- package/dist/mobile-viewport.d.ts +3 -0
- package/dist/mobile-viewport.js +37 -0
- package/dist/model-catalog.d.ts +118 -0
- package/dist/model-catalog.js +376 -0
- package/dist/models-cli.d.ts +102 -0
- package/dist/models-cli.js +66 -0
- package/dist/models-ui.d.ts +34 -0
- package/dist/models-ui.js +68 -0
- package/dist/modes.d.ts +80 -0
- package/dist/modes.js +182 -0
- package/dist/monitoring-settings.d.ts +44 -0
- package/dist/monitoring-settings.js +173 -0
- package/dist/monitoring-ui.d.ts +20 -0
- package/dist/monitoring-ui.js +51 -0
- package/dist/monitoring.d.ts +34 -0
- package/dist/monitoring.js +221 -0
- package/dist/names.d.ts +67 -0
- package/dist/names.js +102 -0
- package/dist/observations.d.ts +40 -0
- package/dist/observations.js +211 -0
- package/dist/oidc.d.ts +71 -0
- package/dist/oidc.js +182 -0
- package/dist/onboard.d.ts +108 -0
- package/dist/onboard.js +325 -0
- package/dist/openrouter-models.d.ts +30 -0
- package/dist/openrouter-models.js +120 -0
- package/dist/operate.d.ts +118 -0
- package/dist/operate.js +11321 -0
- package/dist/peek-cli.d.ts +97 -0
- package/dist/peek-cli.js +215 -0
- package/dist/peek.d.ts +134 -0
- package/dist/peek.js +578 -0
- package/dist/phase-routing.d.ts +307 -0
- package/dist/phase-routing.js +658 -0
- package/dist/plan-auto.d.ts +14 -0
- package/dist/plan-auto.js +92 -0
- package/dist/plan.d.ts +186 -0
- package/dist/plan.js +401 -0
- package/dist/planner-source.d.ts +251 -0
- package/dist/planner-source.js +460 -0
- package/dist/planner.d.ts +75 -0
- package/dist/planner.js +992 -0
- package/dist/policy-ui.d.ts +20 -0
- package/dist/policy-ui.js +41 -0
- package/dist/policy.d.ts +111 -0
- package/dist/policy.js +231 -0
- package/dist/prepared-evidence.d.ts +16 -0
- package/dist/prepared-evidence.js +96 -0
- package/dist/pricing.d.ts +45 -0
- package/dist/pricing.js +77 -0
- package/dist/principal.d.ts +42 -0
- package/dist/principal.js +82 -0
- package/dist/probe.d.ts +46 -0
- package/dist/probe.js +79 -0
- package/dist/process-custody.d.ts +17 -0
- package/dist/process-custody.js +91 -0
- package/dist/process-liveness.d.ts +10 -0
- package/dist/process-liveness.js +81 -0
- package/dist/process-recovery-anchor.d.ts +75 -0
- package/dist/process-recovery-anchor.js +188 -0
- package/dist/process-recovery-coalition.d.ts +25 -0
- package/dist/process-recovery-coalition.js +105 -0
- package/dist/process-recovery-eligibility.d.ts +178 -0
- package/dist/process-recovery-eligibility.js +170 -0
- package/dist/process-recovery-native.d.ts +172 -0
- package/dist/process-recovery-native.js +592 -0
- package/dist/process-recovery-provenance.d.ts +163 -0
- package/dist/process-recovery-provenance.js +292 -0
- package/dist/process-recovery-services.d.ts +56 -0
- package/dist/process-recovery-services.js +191 -0
- package/dist/process-recovery-settlement.d.ts +32 -0
- package/dist/process-recovery-settlement.js +132 -0
- package/dist/process-recovery.d.ts +41 -0
- package/dist/process-recovery.js +91 -0
- package/dist/process-tree.d.ts +41 -0
- package/dist/process-tree.js +264 -0
- package/dist/project-access.d.ts +14 -0
- package/dist/project-access.js +34 -0
- package/dist/project-cli.d.ts +18 -0
- package/dist/project-cli.js +104 -0
- package/dist/project-delete-ui.d.ts +22 -0
- package/dist/project-delete-ui.js +48 -0
- package/dist/project-delete.d.ts +55 -0
- package/dist/project-delete.js +413 -0
- package/dist/project-knowledge.d.ts +111 -0
- package/dist/project-knowledge.js +241 -0
- package/dist/project-learning.d.ts +100 -0
- package/dist/project-learning.js +438 -0
- package/dist/project-memory.d.ts +81 -0
- package/dist/project-memory.js +264 -0
- package/dist/project-skills.d.ts +152 -0
- package/dist/project-skills.js +660 -0
- package/dist/project-tools.d.ts +219 -0
- package/dist/project-tools.js +796 -0
- package/dist/project.d.ts +61 -0
- package/dist/project.js +125 -0
- package/dist/prompt.d.ts +22 -0
- package/dist/prompt.js +72 -0
- package/dist/proof.d.ts +513 -0
- package/dist/proof.js +1140 -0
- package/dist/proposal.d.ts +82 -0
- package/dist/proposal.js +210 -0
- package/dist/provider-connection.d.ts +22 -0
- package/dist/provider-connection.js +98 -0
- package/dist/provider-limits.d.ts +35 -0
- package/dist/provider-limits.js +95 -0
- package/dist/provider.d.ts +279 -0
- package/dist/provider.js +935 -0
- package/dist/publish.d.ts +107 -0
- package/dist/publish.js +650 -0
- package/dist/pulls.d.ts +118 -0
- package/dist/pulls.js +240 -0
- package/dist/push.d.ts +95 -0
- package/dist/push.js +353 -0
- package/dist/quality.d.ts +8 -0
- package/dist/quality.js +6 -0
- package/dist/recipe-ui.d.ts +18 -0
- package/dist/recipe-ui.js +152 -0
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.js +332 -0
- package/dist/remote.d.ts +67 -0
- package/dist/remote.js +122 -0
- package/dist/render.d.ts +64 -0
- package/dist/render.js +587 -0
- package/dist/report-summary.d.ts +13 -0
- package/dist/report-summary.js +15 -0
- package/dist/repos.d.ts +65 -0
- package/dist/repos.js +277 -0
- package/dist/repository-context-ui.d.ts +2 -0
- package/dist/repository-context-ui.js +10 -0
- package/dist/repository-context.d.ts +67 -0
- package/dist/repository-context.js +376 -0
- package/dist/restart-certification.d.ts +134 -0
- package/dist/restart-certification.js +226 -0
- package/dist/result-actions.d.ts +28 -0
- package/dist/result-actions.js +270 -0
- package/dist/result-completion.d.ts +18 -0
- package/dist/result-completion.js +66 -0
- package/dist/result-review.d.ts +213 -0
- package/dist/result-review.js +430 -0
- package/dist/retention-ui.d.ts +12 -0
- package/dist/retention-ui.js +39 -0
- package/dist/retention.d.ts +72 -0
- package/dist/retention.js +288 -0
- package/dist/review-context.d.ts +243 -0
- package/dist/review-context.js +1043 -0
- package/dist/review-evidence.d.ts +33 -0
- package/dist/review-evidence.js +56 -0
- package/dist/reviewer.d.ts +97 -0
- package/dist/reviewer.js +202 -0
- package/dist/routine.d.ts +228 -0
- package/dist/routine.js +764 -0
- package/dist/runner.d.ts +266 -0
- package/dist/runner.js +399 -0
- package/dist/scan.d.ts +32 -0
- package/dist/scan.js +92 -0
- package/dist/scope.d.ts +695 -0
- package/dist/scope.js +1176 -0
- package/dist/scout-report.d.ts +42 -0
- package/dist/scout-report.js +108 -0
- package/dist/scout.d.ts +69 -0
- package/dist/scout.js +351 -0
- package/dist/serve.d.ts +313 -0
- package/dist/serve.js +21776 -0
- package/dist/session-brief.d.ts +8 -0
- package/dist/session-brief.js +24 -0
- package/dist/session-cli.d.ts +13 -0
- package/dist/session-cli.js +284 -0
- package/dist/session-contract.d.ts +183 -0
- package/dist/session-contract.js +122 -0
- package/dist/session-http.d.ts +19 -0
- package/dist/session-http.js +154 -0
- package/dist/session-server.d.ts +12 -0
- package/dist/session-server.js +37 -0
- package/dist/session-service.d.ts +22 -0
- package/dist/session-service.js +157 -0
- package/dist/setup-guide.d.ts +39 -0
- package/dist/setup-guide.js +93 -0
- package/dist/sign-in-guard.d.ts +45 -0
- package/dist/sign-in-guard.js +74 -0
- package/dist/skills-ui.d.ts +11 -0
- package/dist/skills-ui.js +58 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.js +276 -0
- package/dist/slack-api.d.ts +55 -0
- package/dist/slack-api.js +229 -0
- package/dist/slack-chat.d.ts +28 -0
- package/dist/slack-chat.js +413 -0
- package/dist/slack-settings.d.ts +7 -0
- package/dist/slack-settings.js +75 -0
- package/dist/slack-state.d.ts +13 -0
- package/dist/slack-state.js +9 -0
- package/dist/slack.d.ts +13 -0
- package/dist/slack.js +167 -0
- package/dist/spend-ui.d.ts +28 -0
- package/dist/spend-ui.js +95 -0
- package/dist/spend.d.ts +142 -0
- package/dist/spend.js +313 -0
- package/dist/sqlite-runtime.d.ts +3 -0
- package/dist/sqlite-runtime.js +6 -0
- package/dist/sso-settings.d.ts +33 -0
- package/dist/sso-settings.js +103 -0
- package/dist/sso-ui.d.ts +22 -0
- package/dist/sso-ui.js +39 -0
- package/dist/storage.d.ts +15 -0
- package/dist/storage.js +84 -0
- package/dist/store.d.ts +7754 -0
- package/dist/store.js +23279 -0
- package/dist/structured-output.d.ts +58 -0
- package/dist/structured-output.js +103 -0
- package/dist/style-asset.d.ts +7 -0
- package/dist/style-asset.js +41 -0
- package/dist/subscription-chat.d.ts +46 -0
- package/dist/subscription-chat.js +275 -0
- package/dist/summary.d.ts +33 -0
- package/dist/summary.js +71 -0
- package/dist/supervisor.mjs +297 -0
- package/dist/surface.d.ts +63 -0
- package/dist/surface.js +352 -0
- package/dist/sync.d.ts +94 -0
- package/dist/sync.js +279 -0
- package/dist/task-composer.d.ts +13 -0
- package/dist/task-composer.js +52 -0
- package/dist/task-control.d.ts +165 -0
- package/dist/task-control.js +214 -0
- package/dist/task-outcome-cli.d.ts +10 -0
- package/dist/task-outcome-cli.js +92 -0
- package/dist/task-text.d.ts +30 -0
- package/dist/task-text.js +41 -0
- package/dist/team-cli.d.ts +15 -0
- package/dist/team-cli.js +618 -0
- package/dist/team-contract.d.ts +92 -0
- package/dist/team-contract.js +1 -0
- package/dist/team-http.d.ts +17 -0
- package/dist/team-http.js +144 -0
- package/dist/team-leads.d.ts +81 -0
- package/dist/team-leads.js +565 -0
- package/dist/team-runtime.d.ts +27 -0
- package/dist/team-runtime.js +264 -0
- package/dist/team-ui.d.ts +3 -0
- package/dist/team-ui.js +9 -0
- package/dist/team-updates.d.ts +8 -0
- package/dist/team-updates.js +57 -0
- package/dist/teammate-admin.d.ts +53 -0
- package/dist/teammate-admin.js +111 -0
- package/dist/teammate-desk.d.ts +60 -0
- package/dist/teammate-desk.js +154 -0
- package/dist/teammate-memory.d.ts +64 -0
- package/dist/teammate-memory.js +196 -0
- package/dist/teammate-question.d.ts +59 -0
- package/dist/teammate-question.js +175 -0
- package/dist/teammate-tools.d.ts +108 -0
- package/dist/teammate-tools.js +271 -0
- package/dist/teammate-week.d.ts +68 -0
- package/dist/teammate-week.js +132 -0
- package/dist/teammate-work.d.ts +76 -0
- package/dist/teammate-work.js +280 -0
- package/dist/teammates-ui.d.ts +20 -0
- package/dist/teammates-ui.js +166 -0
- package/dist/teammates.d.ts +180 -0
- package/dist/teammates.js +253 -0
- package/dist/teams-api.d.ts +33 -0
- package/dist/teams-api.js +204 -0
- package/dist/teams-chat.d.ts +26 -0
- package/dist/teams-chat.js +215 -0
- package/dist/teams-settings.d.ts +12 -0
- package/dist/teams-settings.js +57 -0
- package/dist/teams.d.ts +21 -0
- package/dist/teams.js +124 -0
- package/dist/telegram-flow.d.ts +49 -0
- package/dist/telegram-flow.js +110 -0
- package/dist/telegram-mate.d.ts +137 -0
- package/dist/telegram-mate.js +707 -0
- package/dist/telegram-progress.d.ts +14 -0
- package/dist/telegram-progress.js +129 -0
- package/dist/telegram-settings.d.ts +9 -0
- package/dist/telegram-settings.js +36 -0
- package/dist/telegram-status.d.ts +75 -0
- package/dist/telegram-status.js +265 -0
- package/dist/telegram-team.d.ts +85 -0
- package/dist/telegram-team.js +359 -0
- package/dist/telegram.d.ts +230 -0
- package/dist/telegram.js +1931 -0
- package/dist/templates.d.ts +65 -0
- package/dist/templates.js +118 -0
- package/dist/tool-launcher.d.ts +1 -0
- package/dist/tool-launcher.js +101 -0
- package/dist/tools-ui.d.ts +30 -0
- package/dist/tools-ui.js +40 -0
- package/dist/transitions-recipes.d.ts +1 -0
- package/dist/transitions-recipes.js +203 -0
- package/dist/tree-proof.d.ts +34 -0
- package/dist/tree-proof.js +58 -0
- package/dist/verification-evidence.d.ts +44 -0
- package/dist/verification-evidence.js +238 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +10 -0
- package/dist/webhooks.d.ts +91 -0
- package/dist/webhooks.js +283 -0
- package/dist/work-index.d.ts +68 -0
- package/dist/work-index.js +384 -0
- package/dist/work-summary.d.ts +57 -0
- package/dist/work-summary.js +116 -0
- package/dist/workspace-motion.d.ts +4 -0
- package/dist/workspace-motion.js +133 -0
- package/dist/workspace-revision.d.ts +19 -0
- package/dist/workspace-revision.js +92 -0
- package/dist/workspace-ui.d.ts +204 -0
- package/dist/workspace-ui.js +411 -0
- package/dist/worktree-notices.d.ts +13 -0
- package/dist/worktree-notices.js +17 -0
- package/dist/worktree.d.ts +249 -0
- package/dist/worktree.js +720 -0
- package/package.json +122 -0
- package/scripts/canary-assertions.mjs +42 -0
- package/scripts/crash-canary.mjs +253 -0
- package/scripts/fixtures/crash-process.mjs +74 -0
- package/scripts/fixtures/pilot-scenarios.mjs +85 -0
- package/scripts/fixtures/restart-service.mjs +27 -0
- package/scripts/launchd-certification.mjs +143 -0
- package/scripts/pilot.mjs +121 -0
- package/scripts/proof-preflight.mjs +140 -0
- package/scripts/provider-canary.mjs +379 -0
- package/scripts/recovery-canary.mjs +135 -0
- package/scripts/restart-certification.mjs +98 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
/** Shared saved replies, explicit confirmations and progress for private chat transports. */
|
|
2
|
+
import { answerChatQuestionPrompt, applyChatQuestionTap, questionParts } from "./teammate-question.js";
|
|
3
|
+
import { channelRepos, resolveChannelMate, proposalPreview, proposalOutcomeText, confirmedLink, confirmedCardText, replyContextFor, mirrorToTaskChat, focusContextFor, taskInCeiling, tooLongText, } from "./chat-channel.js";
|
|
4
|
+
import { MATE_MESSAGE_MAX_CHARS, runMateTurn } from "./mate.js";
|
|
5
|
+
import { confirmMateProposal, dismissMateProposal, } from "./mate-doors.js";
|
|
6
|
+
import { ceilingDigestOf, verifyApproverStanding } from "./principal.js";
|
|
7
|
+
import { ChatState, ChatDeliveryError, chatHash, } from "./chat-delivery-state.js";
|
|
8
|
+
import { answerChatFlowPrompt, applyChatFlowTap, flowDecisionParts } from "./chat-flow.js";
|
|
9
|
+
import { connectChannel, FLOW_WORDS, takeChannelMessage, watchedChannel } from "./chat-inbox.js";
|
|
10
|
+
import { triggerConfigOf } from "./flow-triggers.js";
|
|
11
|
+
import { telegramProgressCard } from "./telegram-progress.js";
|
|
12
|
+
import { phoneText, PHONE_HELP, phoneCommand, phoneStatus, phoneTaskView, phoneTaskChoices, phoneTaskListText, resolvePhoneTask, phoneFocusText, PHONE_NO_MATCH, PHONE_BACK_TO_LEAD } from "./telegram-status.js";
|
|
13
|
+
import { applyRoomInbound, conversationRow, roomCardApprover, roomCommand, roomGrantAllowed, roomMessagesAfter, roomMessageText, teamDomain } from "./chat-rooms.js";
|
|
14
|
+
import { isTelegramProgressNotification, proposalTaskOf } from "./store.js";
|
|
15
|
+
import { messageTeammate } from "./teammate-desk.js";
|
|
16
|
+
import { CHAT_APP_NAMES } from "./flow-triggers.js";
|
|
17
|
+
export const chatObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v)
|
|
18
|
+
? v
|
|
19
|
+
: {};
|
|
20
|
+
const object = chatObject;
|
|
21
|
+
const nowOf = (options) => options.clock?.() ?? new Date();
|
|
22
|
+
export const splitChatText = (text, size = 2800) => {
|
|
23
|
+
const parts = [];
|
|
24
|
+
for (let at = 0; at < text.length;) {
|
|
25
|
+
let end = Math.min(at + size, text.length);
|
|
26
|
+
if (end < text.length && /[\uD800-\uDBFF]/.test(text[end - 1]))
|
|
27
|
+
end--;
|
|
28
|
+
parts.push(text.slice(at, end));
|
|
29
|
+
at = end;
|
|
30
|
+
}
|
|
31
|
+
return parts.length ? parts : ["No reply was recorded."];
|
|
32
|
+
};
|
|
33
|
+
export async function channelAccess(options, binding, ceiling) {
|
|
34
|
+
const state = options.state;
|
|
35
|
+
if (!options.current() ||
|
|
36
|
+
!state.owns(options.identity.installation, options.owner, nowOf(options)) ||
|
|
37
|
+
!state.live(binding))
|
|
38
|
+
throw new ChatDeliveryError("Chat access changed");
|
|
39
|
+
const repos = channelRepos(options.store, binding.approver, await options.readProjects());
|
|
40
|
+
if (ceiling !== undefined && ceilingDigestOf(repos) !== ceiling)
|
|
41
|
+
throw new ChatDeliveryError("Connected projects changed");
|
|
42
|
+
if (!(await options.member(binding.member, binding.channel))) {
|
|
43
|
+
state.revoke(options.identity.installation, nowOf(options));
|
|
44
|
+
throw new ChatDeliveryError(`${options.label} account access changed`);
|
|
45
|
+
}
|
|
46
|
+
// The network wait above can outlive a local disconnect or lease.
|
|
47
|
+
if (!options.current() ||
|
|
48
|
+
!state.live(binding) ||
|
|
49
|
+
!state.owns(options.identity.installation, options.owner, nowOf(options)))
|
|
50
|
+
throw new ChatDeliveryError("Chat access changed");
|
|
51
|
+
const latest = channelRepos(options.store, binding.approver, await options.readProjects());
|
|
52
|
+
if (ceilingDigestOf(latest) !== ceilingDigestOf(repos))
|
|
53
|
+
throw new ChatDeliveryError("Connected projects changed");
|
|
54
|
+
if (!state.live(binding) ||
|
|
55
|
+
!options.current() ||
|
|
56
|
+
!state.owns(options.identity.installation, options.owner, nowOf(options)))
|
|
57
|
+
throw new ChatDeliveryError("Chat access changed");
|
|
58
|
+
return latest;
|
|
59
|
+
}
|
|
60
|
+
export async function processChatEvent(options) {
|
|
61
|
+
const { store, identity } = options, state = options.state, now = nowOf(options);
|
|
62
|
+
if (!state.owns(identity.installation, options.owner, now) ||
|
|
63
|
+
!options.current())
|
|
64
|
+
return false;
|
|
65
|
+
const event = state.next(identity.installation, now);
|
|
66
|
+
if (!event)
|
|
67
|
+
return false;
|
|
68
|
+
try {
|
|
69
|
+
if (event.kind === "pair") {
|
|
70
|
+
if (!(await options.member(event.member, event.channel))) {
|
|
71
|
+
state.finish(event.id, true);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (!state.owns(identity.installation, options.owner, nowOf(options)) ||
|
|
75
|
+
!options.current())
|
|
76
|
+
return false;
|
|
77
|
+
const paired = state.pair(identity, String(object(JSON.parse(event.payload)).hash), event.member, event.channel, nowOf(options));
|
|
78
|
+
if (!paired) {
|
|
79
|
+
state.finish(event.id, true);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
state
|
|
83
|
+
.prepare("UPDATE chat_event SET binding=? WHERE id=?")
|
|
84
|
+
.run(paired.id, event.id);
|
|
85
|
+
state.plan(event.id, [
|
|
86
|
+
{
|
|
87
|
+
text: "Connected to Toolroll. Ask about a project, review a result, or describe what you want done. I’ll show any proposed change before you confirm it.",
|
|
88
|
+
},
|
|
89
|
+
], nowOf(options));
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
// A channel as a flow's inbox (v89): "flow 12" connects it, and after that its messages are cards.
|
|
93
|
+
if (event.kind === "message" && await channelInboxEvent(options, event))
|
|
94
|
+
return true;
|
|
95
|
+
const binding = event.binding === null ? null : state.bindingById(event.binding);
|
|
96
|
+
if (!binding) {
|
|
97
|
+
state.finish(event.id, true);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
const repos = await channelAccess(options, binding);
|
|
101
|
+
if (event.kind === "action") {
|
|
102
|
+
applyChatAction(options, event, binding, repos);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
const input = object(JSON.parse(event.payload));
|
|
106
|
+
if (typeof input.unsupported === "string") {
|
|
107
|
+
state.plan(event.id, [{ text: input.unsupported }], nowOf(options));
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const text = String(input.text ?? "");
|
|
111
|
+
// A teammate's question asked for this person's next message (v93): it is the answer.
|
|
112
|
+
if (answerChatQuestionPrompt({ store, state, label: options.label }, event, binding, { text, ...(typeof input.originalLength === "number" ? { originalLength: input.originalLength } : {}) }, nowOf(options)))
|
|
113
|
+
return true;
|
|
114
|
+
// A flow decision asked for this person's next message (Edit, Send back): it is the draft or the note.
|
|
115
|
+
if (answerChatFlowPrompt({ store, state, label: options.label }, event, binding, { text, ...(typeof input.originalLength === "number" ? { originalLength: input.originalLength } : {}) }, repos, nowOf(options)))
|
|
116
|
+
return true;
|
|
117
|
+
// A message to a teammate by name (v96), in someone's own chat with Toolroll: a card on its desk.
|
|
118
|
+
if (event.channel === binding.channel) {
|
|
119
|
+
const handed = messageTeammate(store, { who: binding.approver, repos, via: CHAT_APP_NAMES[state.channel] ?? "Chat" }, text, nowOf(options));
|
|
120
|
+
if (handed !== null) {
|
|
121
|
+
state.plan(event.id, [{ text: handed.said, ...(handed.link === undefined ? {} : { link: handed.link }) }], nowOf(options));
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// The task this message's turn is about, once chosen (kept on the event,
|
|
126
|
+
// so a reply planned after a restart names the same task).
|
|
127
|
+
let about = typeof object(input.about).task === "string" ? { task: String(object(input.about).task), run: typeof object(input.about).run === "number" ? Number(object(input.about).run) : null } : null;
|
|
128
|
+
// Read-only commands answer from the database, never from a model.
|
|
129
|
+
const command = phoneCommand(text);
|
|
130
|
+
if (command !== null) {
|
|
131
|
+
const now_ = nowOf(options);
|
|
132
|
+
// The task this chat chose to talk about (/tasks, /task <name>), if any.
|
|
133
|
+
const surface = options.label.toLowerCase();
|
|
134
|
+
const focusedId = store.chatFocus(surface, binding.id);
|
|
135
|
+
const focusedTitle = focusedId === null ? null : phoneText(store.getTask(focusedId)?.title ?? focusedId, 64);
|
|
136
|
+
if (command.kind === "help")
|
|
137
|
+
state.plan(event.id, [{ text: PHONE_HELP }], now_);
|
|
138
|
+
else if (command.kind === "status")
|
|
139
|
+
state.plan(event.id, [{ text: phoneStatus(store, repos, now_, focusedTitle) }], now_);
|
|
140
|
+
else if (command.kind === "tasks")
|
|
141
|
+
state.plan(event.id, [{ text: phoneTaskListText(phoneTaskChoices(store, repos, now_), focusedTitle) }], now_);
|
|
142
|
+
else if (command.kind === "lead") {
|
|
143
|
+
store.setChatFocus(surface, binding.id, null, now_);
|
|
144
|
+
state.plan(event.id, [{ text: PHONE_BACK_TO_LEAD }], now_);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const pick = resolvePhoneTask(store, repos, now_, command.id);
|
|
148
|
+
if (pick.kind === "one") {
|
|
149
|
+
store.setChatFocus(surface, binding.id, pick.id, now_);
|
|
150
|
+
const view = phoneTaskView(store, repos, pick.view, now_);
|
|
151
|
+
// The status names its task (and result): a reply to it is about that task.
|
|
152
|
+
state.plan(event.id, [{ text: phoneFocusText(view.text), ...(view.link === null ? {} : { link: view.link }), task: pick.view, ...(view.run === null ? {} : { run: view.run }) }], now_);
|
|
153
|
+
}
|
|
154
|
+
else if (pick.kind === "many") {
|
|
155
|
+
state.plan(event.id, [{ text: ["Several tasks match. Add a word from the title, or send one of these:", ...pick.choices.map(one => `• ${one.title} — /task ${one.id}`)].join("\n") }], now_);
|
|
156
|
+
}
|
|
157
|
+
else
|
|
158
|
+
state.plan(event.id, [{ text: PHONE_NO_MATCH }], now_);
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
// The team layer: `/team` anywhere, everything in a followed room, and a
|
|
163
|
+
// DM that chose a conversation. Its replies are planned like any other.
|
|
164
|
+
const room = state.room(identity.installation, event.channel);
|
|
165
|
+
const isRoom = event.channel !== binding.channel;
|
|
166
|
+
if (isRoom || room !== null || roomCommand(text) !== null) {
|
|
167
|
+
const replies = [];
|
|
168
|
+
const registry = await options.readProjects();
|
|
169
|
+
const consumed = applyRoomInbound({
|
|
170
|
+
store, channel: state.channel, backend: state.roomBackend(identity.installation, now), chatId: event.channel, isGroup: isRoom,
|
|
171
|
+
sender: { approver: binding.approver, generation: binding.generation, binding: binding.id }, updateKey: event.id, text,
|
|
172
|
+
projects: registry, origin: options.origin(), now, report: { ignored: 0 },
|
|
173
|
+
say: (reply, link) => replies.push({ text: reply, ...(link ? { link } : {}), ...(isRoom ? { channel: event.channel } : {}) }),
|
|
174
|
+
});
|
|
175
|
+
if (consumed) {
|
|
176
|
+
state.plan(event.id, replies, now);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (text.length > MATE_MESSAGE_MAX_CHARS) {
|
|
181
|
+
state.plan(event.id, [
|
|
182
|
+
{
|
|
183
|
+
text: tooLongText(Number(object(JSON.parse(event.payload)).originalLength) ||
|
|
184
|
+
text.length),
|
|
185
|
+
},
|
|
186
|
+
], nowOf(options));
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
const request = chatHash(`${options.state.channel}:${binding.id}:${event.id}`).slice(0, 32);
|
|
190
|
+
let receipt = event.session === null
|
|
191
|
+
? null
|
|
192
|
+
: store.mateRequestReceipt(event.session, request);
|
|
193
|
+
if (!receipt) {
|
|
194
|
+
if (new Date(event.created).getTime() + 600_000 < now.getTime()) {
|
|
195
|
+
state.plan(event.id, [
|
|
196
|
+
{
|
|
197
|
+
text: "This message waited too long to start. Send it again when you’re ready.",
|
|
198
|
+
},
|
|
199
|
+
], now);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
const resolved = resolveChannelMate(store, { approver: binding.approver, approverGeneration: binding.generation }, repos, now);
|
|
203
|
+
if (!resolved.ok) {
|
|
204
|
+
state.plan(event.id, [
|
|
205
|
+
{
|
|
206
|
+
text: (resolved.said ?? "This chat is no longer paired.")
|
|
207
|
+
.replaceAll("this phone", options.label)
|
|
208
|
+
.replaceAll("the phone", options.label),
|
|
209
|
+
},
|
|
210
|
+
], now);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
if (event.session !== null && event.session !== resolved.session.id) {
|
|
214
|
+
state.plan(event.id, [{ text: "Your chat session changed. Send your message again." }], now);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
state
|
|
218
|
+
.prepare("UPDATE chat_event SET session=? WHERE id=? AND state='queued'")
|
|
219
|
+
.run(resolved.session.id, event.id);
|
|
220
|
+
const contexts = state
|
|
221
|
+
.prepare("SELECT p.payload FROM chat_part p JOIN chat_event e ON e.id=p.event WHERE e.binding=? AND e.channel=? AND (p.message=? OR e.thread=?) AND p.state='sent' ORDER BY p.id DESC LIMIT 100")
|
|
222
|
+
.all(binding.id, binding.channel, event.thread, event.thread)
|
|
223
|
+
.map((row) => JSON.parse(String(row.payload)))
|
|
224
|
+
// A card is about the task it names, or the one confirming it filed.
|
|
225
|
+
.map((c) => {
|
|
226
|
+
if (c.task || c.proposal === undefined)
|
|
227
|
+
return c;
|
|
228
|
+
const about = proposalTaskOf(store.getMateProposal(c.proposal));
|
|
229
|
+
return about === null ? c : { ...c, task: about.task, ...(about.run === null ? {} : { run: about.run }) };
|
|
230
|
+
});
|
|
231
|
+
const targets = [
|
|
232
|
+
...new Map(contexts
|
|
233
|
+
.filter((c) => c.task)
|
|
234
|
+
.map((c) => [`${c.task}:${c.run ?? ""}`, c])).values(),
|
|
235
|
+
];
|
|
236
|
+
if (targets.length > 1) {
|
|
237
|
+
state.plan(event.id, [
|
|
238
|
+
{
|
|
239
|
+
text: "This thread is about more than one task. Reply to the message about the one you mean, or send /tasks to pick one.",
|
|
240
|
+
},
|
|
241
|
+
], nowOf(options));
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
// No reply target: the task this chat chose, when it still exists here.
|
|
245
|
+
const focused = targets.length === 0 ? store.chatFocus(options.label.toLowerCase(), binding.id) : null;
|
|
246
|
+
const focusTask = focused !== null && taskInCeiling(store, focused, repos) ? focused : null;
|
|
247
|
+
const context = targets[0] ?? (focusTask === null ? null : { text: "", task: focusTask, run: null });
|
|
248
|
+
if (context?.task) {
|
|
249
|
+
about = { task: context.task, run: context.run ?? null };
|
|
250
|
+
state
|
|
251
|
+
.prepare("UPDATE chat_event SET payload=? WHERE id=? AND state='queued'")
|
|
252
|
+
.run(JSON.stringify({ ...input, about }), event.id);
|
|
253
|
+
}
|
|
254
|
+
const outcome = await runMateTurn({
|
|
255
|
+
store,
|
|
256
|
+
who: resolved.who,
|
|
257
|
+
session: resolved.session,
|
|
258
|
+
thread: resolved.thread,
|
|
259
|
+
config: resolved.config,
|
|
260
|
+
key: null,
|
|
261
|
+
message: text,
|
|
262
|
+
requestId: request,
|
|
263
|
+
...(context?.task
|
|
264
|
+
? { context: focusTask !== null && targets.length === 0 ? focusContextFor(context.task) : replyContextFor(context.task, context.run ?? null) }
|
|
265
|
+
: {}),
|
|
266
|
+
...(options.subscriptionRunner
|
|
267
|
+
? { subscriptionRunner: options.subscriptionRunner }
|
|
268
|
+
: {}),
|
|
269
|
+
clock: () => nowOf(options),
|
|
270
|
+
evidenceRoot: options.evidenceRoot,
|
|
271
|
+
mediaDelivery: "documents",
|
|
272
|
+
revalidate: async () => {
|
|
273
|
+
try {
|
|
274
|
+
await channelAccess(options, binding, resolved.who.ceilingDigest);
|
|
275
|
+
return { ok: true };
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
reason: `${options.label} access could not be verified`,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
if (outcome.ok && !outcome.replayed && context?.task)
|
|
286
|
+
mirrorToTaskChat(store, resolved.who, context.task, options.label, text, outcome.reply, nowOf(options));
|
|
287
|
+
if (!state.owns(identity.installation, options.owner, nowOf(options)) ||
|
|
288
|
+
!options.current())
|
|
289
|
+
return false;
|
|
290
|
+
if (!outcome.ok &&
|
|
291
|
+
"refused" in outcome &&
|
|
292
|
+
outcome.refused === "concurrent") {
|
|
293
|
+
state.defer(event.id, "Assistant is answering another message", new Date(nowOf(options).getTime() + 5000));
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
receipt = store.mateRequestReceipt(resolved.session.id, request);
|
|
297
|
+
if (!receipt) {
|
|
298
|
+
state.plan(event.id, [
|
|
299
|
+
{
|
|
300
|
+
text: !outcome.ok
|
|
301
|
+
? phoneText(outcome.message, 1000)
|
|
302
|
+
: "The assistant did not save a reply. Send your message again.",
|
|
303
|
+
},
|
|
304
|
+
], nowOf(options));
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
store.sweepStaleMateTurns(nowOf(options));
|
|
309
|
+
const turn = store.getMateTurn(receipt.turn);
|
|
310
|
+
if (turn?.state === "running" || turn?.state === "queued") {
|
|
311
|
+
state.defer(event.id, "Reply is still running", new Date(nowOf(options).getTime() + 5000));
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
if (turn?.state !== "answered") {
|
|
315
|
+
state.plan(event.id, [
|
|
316
|
+
{
|
|
317
|
+
text: "The assistant’s reply did not finish. No proposed changes were kept. Send your message again to retry.",
|
|
318
|
+
},
|
|
319
|
+
], nowOf(options));
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
const session = store.getMateSession(turn.session);
|
|
323
|
+
if (!session) {
|
|
324
|
+
state.finish(event.id, true);
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
await channelAccess(options, binding, session.ceilingDigest);
|
|
328
|
+
const reply = store
|
|
329
|
+
.listMateMessages(turn.thread, 200)
|
|
330
|
+
.find((message) => message.turn === turn.id && message.role === "assistant")?.text ?? "The reply is no longer in the saved thread.";
|
|
331
|
+
// The lead's answer about one task names it: a reply to it stays on that task.
|
|
332
|
+
const parts = splitChatText(reply, options.partSize).map((text) => (about === null ? { text } : { text, task: about.task, ...(about.run === null ? {} : { run: about.run }) }));
|
|
333
|
+
for (const image of store.listMateTurnEvidence(turn.id))
|
|
334
|
+
parts.push({
|
|
335
|
+
text: image.caption,
|
|
336
|
+
image: {
|
|
337
|
+
taskId: image.taskId,
|
|
338
|
+
run: image.run,
|
|
339
|
+
artifact: image.artifact,
|
|
340
|
+
sha256: image.sha256,
|
|
341
|
+
},
|
|
342
|
+
task: image.taskId,
|
|
343
|
+
run: image.run,
|
|
344
|
+
});
|
|
345
|
+
for (const proposal of store
|
|
346
|
+
.listMateProposals(turn.thread, ["pending"])
|
|
347
|
+
.filter((p) => p.turn === turn.id))
|
|
348
|
+
parts.push({ text: "", proposal: proposal.id });
|
|
349
|
+
state.plan(event.id, parts, nowOf(options));
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
const problem = error instanceof ChatDeliveryError
|
|
354
|
+
? error
|
|
355
|
+
: new ChatDeliveryError(`${options.label} is waiting to retry`);
|
|
356
|
+
state
|
|
357
|
+
.prepare("UPDATE chat_runtime SET problem=? WHERE installation=? AND owner=?")
|
|
358
|
+
.run(problem.message, identity.installation, options.owner);
|
|
359
|
+
state.defer(event.id, problem.message, new Date(nowOf(options).getTime() + problem.retryMs));
|
|
360
|
+
if (problem.code === "ratelimited")
|
|
361
|
+
state
|
|
362
|
+
.prepare("UPDATE chat_runtime SET retry_at=? WHERE installation=?")
|
|
363
|
+
.run(new Date(nowOf(options).getTime() + problem.retryMs).toISOString(), identity.installation);
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* A message in a channel (never a DM) that connects the channel to a flow,
|
|
369
|
+
* or that the channel's flow takes as a card. False: not for an inbox.
|
|
370
|
+
*/
|
|
371
|
+
async function channelInboxEvent(options, event) {
|
|
372
|
+
const { store, identity } = options, state = options.state, app = state.channel;
|
|
373
|
+
const binding = event.binding === null ? null : state.bindingById(event.binding);
|
|
374
|
+
if (binding !== null && event.channel === binding.channel)
|
|
375
|
+
return false;
|
|
376
|
+
const input = object(JSON.parse(event.payload));
|
|
377
|
+
const text = String(input.text ?? "");
|
|
378
|
+
if (FLOW_WORDS.test(text.trim())) {
|
|
379
|
+
// Only a paired approver connects a channel, and only to a flow in their projects.
|
|
380
|
+
if (binding === null || !state.live(binding)) {
|
|
381
|
+
state.finish(event.id, true);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
const repos = await channelAccess(options, binding);
|
|
385
|
+
const said = connectChannel(store, { app, installation: identity.installation, conversation: event.channel, binding, text, repos,
|
|
386
|
+
followsConversation: state.room(identity.installation, event.channel) !== null }, nowOf(options));
|
|
387
|
+
state.plan(event.id, [{ text: said, channel: event.channel }], nowOf(options));
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
const trigger = watchedChannel(store, app, identity.installation, event.channel);
|
|
391
|
+
const config = trigger === null ? null : triggerConfigOf(trigger);
|
|
392
|
+
if (trigger === null || config?.kind !== "chat")
|
|
393
|
+
return false;
|
|
394
|
+
// What the bot says goes through the chat of whoever connected the channel; if they're gone, nothing is taken.
|
|
395
|
+
const grant = state.bindingById(config.binding);
|
|
396
|
+
if (grant === null || !state.live(grant)) {
|
|
397
|
+
state.finish(event.id, true);
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
// Teams names a thread by its first message on the conversation; Slack and Discord on the event.
|
|
401
|
+
const root = app === "teams" ? /;messageid=([0-9]+)$/.exec(event.channel)?.[1] ?? event.ts : event.thread;
|
|
402
|
+
const taken = takeChannelMessage(store, trigger, { app, conversation: event.channel, ts: event.ts, thread: root, text, who: binding?.approver ?? "someone" }, nowOf(options));
|
|
403
|
+
if (taken.said === null) {
|
|
404
|
+
state.finish(event.id);
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
state.prepare("UPDATE chat_event SET binding=? WHERE id=?").run(grant.id, event.id);
|
|
408
|
+
state.plan(event.id, [{ text: taken.said, channel: event.channel, ...(taken.link === undefined ? {} : { link: taken.link }) }], nowOf(options));
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
/** Bound to the paired member, exact channel/message, pending proposal and current ceiling. */
|
|
412
|
+
export function applyChatAction(options, event, binding, repos) {
|
|
413
|
+
const state = options.state, { store } = options, now = nowOf(options), signals = [];
|
|
414
|
+
store.transact(() => {
|
|
415
|
+
if (!state.owns(options.identity.installation, options.owner, now) ||
|
|
416
|
+
!options.current() ||
|
|
417
|
+
!state.live(binding) ||
|
|
418
|
+
event.member !== binding.member ||
|
|
419
|
+
(event.channel !== binding.channel && state.room(options.identity.installation, event.channel)?.kind !== "group"))
|
|
420
|
+
return;
|
|
421
|
+
const token = String(object(JSON.parse(event.payload)).token);
|
|
422
|
+
// A flow decision's button (v88) is answered by the flow's own door.
|
|
423
|
+
if (applyChatFlowTap({ store, state, label: options.label }, event, binding, token, repos, now))
|
|
424
|
+
return;
|
|
425
|
+
// A teammate's question's button (v93) is answered by the question's own door.
|
|
426
|
+
if (applyChatQuestionTap({ store, state, label: options.label }, event, binding, token, now))
|
|
427
|
+
return;
|
|
428
|
+
const action = state
|
|
429
|
+
.prepare("SELECT a.*,p.message,e.binding,e.channel,e.thread FROM chat_action a JOIN chat_part p ON p.id=a.part JOIN chat_event e ON e.id=p.event WHERE token=?")
|
|
430
|
+
.get(token);
|
|
431
|
+
const invalid = !action ||
|
|
432
|
+
action.binding !== binding.id ||
|
|
433
|
+
action.channel !== event.channel ||
|
|
434
|
+
action.message !== event.ts ||
|
|
435
|
+
(options.state.channel === "slack" && action.thread !== event.thread) ||
|
|
436
|
+
action.consumed !== null ||
|
|
437
|
+
String(action.expires) <= now.toISOString();
|
|
438
|
+
if (invalid) {
|
|
439
|
+
state.plan(event.id, [
|
|
440
|
+
{
|
|
441
|
+
text: "That button expired or was already used. Ask for the current state before trying again.",
|
|
442
|
+
},
|
|
443
|
+
], now);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const proposal = store.getMateProposal(Number(action.proposal));
|
|
447
|
+
const verified = verifyApproverStanding(store, binding.approver, binding.generation, repos);
|
|
448
|
+
if (!proposal || !verified.ok) {
|
|
449
|
+
state.finish(event.id, true);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
let content = {
|
|
453
|
+
text: "",
|
|
454
|
+
proposal: proposal.id,
|
|
455
|
+
edit: event.ts,
|
|
456
|
+
};
|
|
457
|
+
const preview = proposalPreview(store, proposal, repos, options.state.channel);
|
|
458
|
+
const phase = String(action.phase);
|
|
459
|
+
if (proposal.state !== "pending")
|
|
460
|
+
content = { text: proposalOutcomeText(proposal), edit: event.ts };
|
|
461
|
+
else if (!preview.buttons ||
|
|
462
|
+
preview.text.length > (options.maxProposal ?? 10_000))
|
|
463
|
+
content = { ...content, text: "Review this action in Toolroll." };
|
|
464
|
+
else if (phase === "cancel") {
|
|
465
|
+
state.tokens(Number(action.part), proposal.id, ["confirm", "dismiss"], now);
|
|
466
|
+
}
|
|
467
|
+
else if (phase === "dismiss") {
|
|
468
|
+
const dismissed = dismissMateProposal(store, verified.who, proposal.id, now);
|
|
469
|
+
content = {
|
|
470
|
+
text: dismissed
|
|
471
|
+
? "Dismissed."
|
|
472
|
+
: "This proposal could not be dismissed. Ask for its current state.",
|
|
473
|
+
edit: event.ts,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
else if (phase === "confirm" &&
|
|
477
|
+
proposal.kind === "answer" &&
|
|
478
|
+
proposal.payload.reversible === false) {
|
|
479
|
+
content = { ...content, phase: "armed" };
|
|
480
|
+
state.tokens(Number(action.part), proposal.id, ["yes", "cancel"], now);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
const outcome = confirmMateProposal(store, verified.who, proposal.id, now, {
|
|
484
|
+
via: options.state.channel,
|
|
485
|
+
evidenceRoot: options.evidenceRoot,
|
|
486
|
+
confirm: phase === "yes",
|
|
487
|
+
...(options.held ? { held: options.held } : {}),
|
|
488
|
+
deferSignal: (signal) => signals.push(signal),
|
|
489
|
+
});
|
|
490
|
+
if (!outcome.ok && outcome.reason === "needs-confirm") {
|
|
491
|
+
content = { ...content, phase: "armed" };
|
|
492
|
+
state.tokens(Number(action.part), proposal.id, ["yes", "cancel"], now);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
content = {
|
|
496
|
+
text: confirmedCardText(store, outcome, proposal, null),
|
|
497
|
+
edit: event.ts,
|
|
498
|
+
...(outcome.ok && outcome.taskId ? { task: outcome.taskId } : {}),
|
|
499
|
+
};
|
|
500
|
+
const link = confirmedLink(store, outcome, proposal, repos);
|
|
501
|
+
if (link)
|
|
502
|
+
content.link = link;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Repaint the original persisted card; retain one placement and fresh tokens.
|
|
506
|
+
state
|
|
507
|
+
.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE id=?")
|
|
508
|
+
.run(JSON.stringify(content), Number(action.part));
|
|
509
|
+
if (!content.proposal)
|
|
510
|
+
state
|
|
511
|
+
.prepare("UPDATE chat_action SET consumed=? WHERE proposal=? AND consumed IS NULL")
|
|
512
|
+
.run(now.toISOString(), proposal.id);
|
|
513
|
+
else
|
|
514
|
+
state
|
|
515
|
+
.prepare("UPDATE chat_action SET consumed=? WHERE token=?")
|
|
516
|
+
.run(now.toISOString(), token);
|
|
517
|
+
state.finish(event.id);
|
|
518
|
+
});
|
|
519
|
+
for (const signal of signals)
|
|
520
|
+
signal();
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Carry new conversation messages to every room that follows one: the
|
|
524
|
+
* lead's replies, teammates' messages (never the room's own), and each
|
|
525
|
+
* reply's pending cards, as ordinary parts sent to the room's channel. A
|
|
526
|
+
* room is a grant from one exact pairing and is rechecked before each
|
|
527
|
+
* message; a room nobody paired can carry waits.
|
|
528
|
+
*/
|
|
529
|
+
export async function planRoomMessages(options) {
|
|
530
|
+
const state = options.state, { store, identity } = options, now = nowOf(options);
|
|
531
|
+
if (!state.owns(identity.installation, options.owner, now) || !options.current())
|
|
532
|
+
return;
|
|
533
|
+
const rooms = state.rooms(identity.installation);
|
|
534
|
+
if (rooms.length === 0)
|
|
535
|
+
return;
|
|
536
|
+
const registry = await options.readProjects();
|
|
537
|
+
if (!state.owns(identity.installation, options.owner, nowOf(options)) || !options.current())
|
|
538
|
+
return;
|
|
539
|
+
const domain = teamDomain(store, registry);
|
|
540
|
+
const backend = state.roomBackend(identity.installation, now);
|
|
541
|
+
const live = state.bindings(identity.installation).filter(one => state.live(one));
|
|
542
|
+
for (const room of rooms) {
|
|
543
|
+
const asRoom = { id: room.id, chatId: room.chat, kind: room.kind, conversation: room.conversation, boundBy: room.boundBy, binding: room.binding, cursor: room.cursor };
|
|
544
|
+
if (!roomGrantAllowed(store, domain, backend, asRoom))
|
|
545
|
+
continue;
|
|
546
|
+
const row = conversationRow(store, room.conversation);
|
|
547
|
+
if (row === null)
|
|
548
|
+
continue;
|
|
549
|
+
for (const message of roomMessagesAfter(store, row.thread, room.cursor)) {
|
|
550
|
+
const text = roomMessageText(message, state.channel, room.chat);
|
|
551
|
+
let carrier = null;
|
|
552
|
+
if (room.kind === "private")
|
|
553
|
+
carrier = live.find(one => one.channel === room.chat) ?? null;
|
|
554
|
+
else {
|
|
555
|
+
const approver = message.turn === null ? null : roomCardApprover(store, room.conversation, message.turn, live.map(one => one.approver));
|
|
556
|
+
const preferred = approver === null ? undefined : live.find(one => one.approver === approver);
|
|
557
|
+
carrier = preferred ?? live.find(one => one.id === room.binding) ?? null;
|
|
558
|
+
}
|
|
559
|
+
if (carrier === null)
|
|
560
|
+
break;
|
|
561
|
+
const parts = [];
|
|
562
|
+
if (text !== null)
|
|
563
|
+
parts.push({ text, channel: room.chat });
|
|
564
|
+
if (message.role === "assistant" && message.turn !== null)
|
|
565
|
+
for (const proposal of store.listMateProposals(row.thread, ["pending"]).filter(one => one.turn === message.turn))
|
|
566
|
+
parts.push({ text: "", proposal: proposal.id, channel: room.chat });
|
|
567
|
+
const id = chatHash(`${state.channel}:room:${room.id}:${message.id}`);
|
|
568
|
+
const target = carrier;
|
|
569
|
+
store.transact(() => {
|
|
570
|
+
if (parts.length > 0 && state.enqueue({ id, installation: identity.installation, binding: target.id, kind: "message", channel: room.chat, member: target.member, ts: "", thread: "", payload: "{}", created: now.toISOString() }))
|
|
571
|
+
state.plan(id, parts, now);
|
|
572
|
+
state.advanceRoomCursor(room.id, message.id);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/** One progress card per exact result; separate urgent facts retain their own review link. */
|
|
578
|
+
export async function planChatNotifications(options) {
|
|
579
|
+
const state = options.state, { store, identity } = options, bindings = state.bindings(identity.installation).filter(one => state.live(one));
|
|
580
|
+
if (bindings.length === 0 ||
|
|
581
|
+
!state.owns(identity.installation, options.owner, nowOf(options)) ||
|
|
582
|
+
!options.current())
|
|
583
|
+
return;
|
|
584
|
+
const registry = await options.readProjects();
|
|
585
|
+
const cursor = Number(state
|
|
586
|
+
.prepare("SELECT notification FROM chat_runtime WHERE installation=?")
|
|
587
|
+
.get(identity.installation)?.notification ?? 0);
|
|
588
|
+
// Every paired person is a destination of their own, under their own ceiling.
|
|
589
|
+
for (const notification of store.notificationsAfter(cursor, 100)) {
|
|
590
|
+
for (const binding of bindings) {
|
|
591
|
+
const repos = channelRepos(store, binding.approver, registry);
|
|
592
|
+
store.transact(() => {
|
|
593
|
+
// One person's notification (v83) goes to that person alone, task or not.
|
|
594
|
+
const personal = notification.recipient !== null;
|
|
595
|
+
if (notification.createdAt >= binding.created &&
|
|
596
|
+
notification.resolvedAt === null &&
|
|
597
|
+
// A flow decision for "anyone who approves" reaches every approver who can see the project.
|
|
598
|
+
(personal ? notification.recipient === binding.approver : notification.taskId || notification.kind === "flow-decision") &&
|
|
599
|
+
notification.project &&
|
|
600
|
+
repos.includes(notification.project)) {
|
|
601
|
+
const run = store.telegramProgressRun(notification);
|
|
602
|
+
const id = chatHash(`${options.state.channel}:notice:${binding.id}:${notification.id}`), now = nowOf(options);
|
|
603
|
+
if (run && notification.taskId && isTelegramProgressNotification(notification)) {
|
|
604
|
+
const card = telegramProgressCard(store, store.getRun(run.id), notification.taskId, notification.project, now, options.evidenceRoot);
|
|
605
|
+
const content = {
|
|
606
|
+
text: card.text,
|
|
607
|
+
task: notification.taskId,
|
|
608
|
+
run: run.id,
|
|
609
|
+
link: card.link,
|
|
610
|
+
};
|
|
611
|
+
const digest = chatHash(JSON.stringify(content));
|
|
612
|
+
const prior = state
|
|
613
|
+
.prepare("SELECT part,digest FROM chat_progress WHERE binding=? AND run=?")
|
|
614
|
+
.get(binding.id, run.id);
|
|
615
|
+
if (prior) {
|
|
616
|
+
if (prior.digest !== digest) {
|
|
617
|
+
state
|
|
618
|
+
.prepare("UPDATE chat_part SET payload=?,state='pending',created=?,next_at=NULL WHERE id=? AND state!='dropped'")
|
|
619
|
+
.run(JSON.stringify(content), now.toISOString(), Number(prior.part));
|
|
620
|
+
state
|
|
621
|
+
.prepare("UPDATE chat_progress SET digest=? WHERE binding=? AND run=?")
|
|
622
|
+
.run(digest, binding.id, run.id);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
state.enqueue({
|
|
627
|
+
id,
|
|
628
|
+
installation: identity.installation,
|
|
629
|
+
binding: binding.id,
|
|
630
|
+
kind: "notice",
|
|
631
|
+
channel: binding.channel,
|
|
632
|
+
member: binding.member,
|
|
633
|
+
ts: "",
|
|
634
|
+
thread: "",
|
|
635
|
+
payload: "{}",
|
|
636
|
+
created: now.toISOString(),
|
|
637
|
+
});
|
|
638
|
+
state.plan(id, [content], now);
|
|
639
|
+
const part = state
|
|
640
|
+
.prepare("SELECT id FROM chat_part WHERE event=?")
|
|
641
|
+
.get(id);
|
|
642
|
+
state
|
|
643
|
+
.prepare("INSERT INTO chat_progress VALUES(?,?,?,?)")
|
|
644
|
+
.run(binding.id, run.id, Number(part.id), digest);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
else if (notification.kind === "flow-decision") {
|
|
648
|
+
// The draft as written, and Approve / Edit / Send back on its last part; a card that moved on is not news.
|
|
649
|
+
const parts = flowDecisionParts(store, notification);
|
|
650
|
+
if (parts !== null) {
|
|
651
|
+
state.enqueue({ id, installation: identity.installation, binding: binding.id, kind: "notice", channel: binding.channel, member: binding.member,
|
|
652
|
+
ts: "", thread: "", payload: "{}", created: now.toISOString() });
|
|
653
|
+
state.plan(id, parts, now);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
else if (notification.kind === "flow-card" && questionParts(store, notification, binding) !== null) {
|
|
657
|
+
// A teammate's question (v93): its options and "Answer in words" on the notice, for the person it asks.
|
|
658
|
+
state.enqueue({ id, installation: identity.installation, binding: binding.id, kind: "notice", channel: binding.channel, member: binding.member,
|
|
659
|
+
ts: "", thread: "", payload: "{}", created: now.toISOString() });
|
|
660
|
+
state.plan(id, questionParts(store, notification, binding), now);
|
|
661
|
+
}
|
|
662
|
+
else if (notification.pushClass !== null || personal) {
|
|
663
|
+
state.enqueue({
|
|
664
|
+
id,
|
|
665
|
+
installation: identity.installation,
|
|
666
|
+
binding: binding.id,
|
|
667
|
+
kind: "notice",
|
|
668
|
+
channel: binding.channel,
|
|
669
|
+
member: binding.member,
|
|
670
|
+
ts: "",
|
|
671
|
+
thread: "",
|
|
672
|
+
payload: "{}",
|
|
673
|
+
created: now.toISOString(),
|
|
674
|
+
});
|
|
675
|
+
state.plan(id, [
|
|
676
|
+
{
|
|
677
|
+
text: phoneText(`${notification.subject}\n\n${notification.body}`, 2500),
|
|
678
|
+
...(notification.taskId ? { task: notification.taskId } : {}),
|
|
679
|
+
...(run ? { run: run.id } : {}),
|
|
680
|
+
...(notification.link
|
|
681
|
+
? { link: { label: personal ? "Open" : "Review", path: notification.link } }
|
|
682
|
+
: {}),
|
|
683
|
+
},
|
|
684
|
+
], now);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
state
|
|
690
|
+
.prepare("UPDATE chat_runtime SET notification=? WHERE installation=?")
|
|
691
|
+
.run(notification.id, identity.installation);
|
|
692
|
+
}
|
|
693
|
+
}
|