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,280 @@
|
|
|
1
|
+
import { decideFlowCard, draftFor, flowCardHref } from "./flow-engine.js";
|
|
2
|
+
import { keptDraft } from "./flow-draft.js";
|
|
3
|
+
import { notifyPeople } from "./flow-people.js";
|
|
4
|
+
import { claudeTurnRunner, parseSoul, readTurn, teammateActor, teammateLabel, TURN_TIMEOUT_MS, turnPrompt } from "./teammates.js";
|
|
5
|
+
import { callName, callOutcome, callWords, inputProblem, makeCall, offeredTools, refreshGrants, ruleFor } from "./teammate-tools.js";
|
|
6
|
+
import { answerSuggestion, considerSuggestion, memoriesFor, remember } from "./teammate-memory.js";
|
|
7
|
+
import { replyToAsker } from "./teammate-desk.js";
|
|
8
|
+
import { claudeMachineBilling } from "./spend.js";
|
|
9
|
+
/** A question waits for its answer this long before the step is due again on its own: never, in practice. */
|
|
10
|
+
export const ASKED = "9999-12-31T00:00:00.000Z";
|
|
11
|
+
/** What "handle" zones with no answers of their own offer: going on to the next zone. */
|
|
12
|
+
const CARRY_ON = "Carry on";
|
|
13
|
+
/** v94: tool calls one visit of a card may make, and turns one step run may take, before it must decide with what it has. */
|
|
14
|
+
export const CALLS_PER_VISIT = 12;
|
|
15
|
+
const TURNS_PER_RUN = 8;
|
|
16
|
+
/** The two answers an ask-first call offers; words instead tell the teammate what to do. */
|
|
17
|
+
export const APPROVE = "approve", DENY = "deny";
|
|
18
|
+
const startOfDay = (now) => { const day = new Date(now); day.setHours(0, 0, 0, 0); return day.toISOString(); };
|
|
19
|
+
/** The Claude model a teammate's turns run on: its own, or the lead chat's Claude model, or Claude's default. */
|
|
20
|
+
export function teammateModel(store, mate) {
|
|
21
|
+
const config = store.getChatConfig();
|
|
22
|
+
return mate.model ?? (config?.provider === "claude-subscription" ? config.model : "default");
|
|
23
|
+
}
|
|
24
|
+
/** Whether a teammate can take a turn now: active, readable, and under its daily limit. */
|
|
25
|
+
export function teammateReady(store, mate, now) {
|
|
26
|
+
if (mate === null)
|
|
27
|
+
return { ok: false, why: "gone" };
|
|
28
|
+
if (mate.state !== "active")
|
|
29
|
+
return { ok: false, why: "paused" };
|
|
30
|
+
if (!parseSoul(mate.soul).ok)
|
|
31
|
+
return { ok: false, why: "its soul file can't be read" };
|
|
32
|
+
if (store.teammateTurnsSince(mate.id, startOfDay(now)) >= mate.dailyTurns)
|
|
33
|
+
return { ok: false, why: `it reached today's limit of ${mate.dailyTurns} turns` };
|
|
34
|
+
// v105: its turns bill as this computer's Claude does with no key from us; billed to a key, a budget holds them.
|
|
35
|
+
const held = store.budgetGate(now)({ project: mate.repo, person: null, teammate: mate.id, agents: [{ provider: "claude", billing: claudeMachineBilling(store.handle) }] });
|
|
36
|
+
if (held.over !== null)
|
|
37
|
+
return { ok: false, why: held.why === "used-up" ? "a monthly budget its work counts toward is used up" : "a monthly budget can't price its work yet" };
|
|
38
|
+
// Sprint 8: its turns run on Claude, with its own model or the lead chat's: the organisation policy has to allow them.
|
|
39
|
+
const disallowed = store.agentPolicyRefusal("claude", teammateModel(store, mate));
|
|
40
|
+
if (disallowed !== null)
|
|
41
|
+
return { ok: false, why: disallowed };
|
|
42
|
+
return { ok: true };
|
|
43
|
+
}
|
|
44
|
+
/** The answers a "Teammate handles it" zone offers, each with the zone it leads to. */
|
|
45
|
+
export function handleAnswers(stage) {
|
|
46
|
+
return stage.routes !== undefined && stage.routes.length > 0 ? stage.routes : [{ answer: CARRY_ON, to: stage.next }];
|
|
47
|
+
}
|
|
48
|
+
/** Everything the turn is told. */
|
|
49
|
+
function contextOf(store, flow, definition, stage, card, mate, tools = [], spent = false) {
|
|
50
|
+
const soul = parseSoul(mate.soul);
|
|
51
|
+
if (!soul.ok)
|
|
52
|
+
throw new Error(soul.problem);
|
|
53
|
+
const title = (id) => definition.stages.find(one => one.id === id)?.title ?? id;
|
|
54
|
+
const draft = stage.kind === "approval" ? draftFor(definition, stage) : null;
|
|
55
|
+
const question = store.teammateQuestionFor(card.id, card.entry);
|
|
56
|
+
// v95: what its people told it lately, and what it kept that fits this card.
|
|
57
|
+
const memory = memoriesFor(store, mate, `${card.title}\n${card.description ?? ""}`, new Date());
|
|
58
|
+
return {
|
|
59
|
+
soul: mate.soul, name: soul.soul.name, role: soul.soul.role, flow: flow.name, zone: stage.title, kind: stage.kind === "approval" ? "decide" : "handle",
|
|
60
|
+
instructions: stage.instructions, draft: draft === null ? null : card.outputs[draft.id] ?? null, canSendBack: stage.onFail !== null,
|
|
61
|
+
person: stage.kind === "approval" ? (stage.toOwner === true ? flow.owner : stage.approver ?? flow.owner) : flow.owner,
|
|
62
|
+
answers: handleAnswers(stage).map(one => one.answer),
|
|
63
|
+
card: {
|
|
64
|
+
title: card.title, description: card.description, source: card.source?.label ?? null,
|
|
65
|
+
earlier: definition.stages.filter(one => one.id !== stage.id && card.outputs[one.id] !== undefined).map(one => ({ zone: one.title, text: card.outputs[one.id] })),
|
|
66
|
+
discussion: store.flowComments(card.id).filter(one => one.kind === "comment").slice(-10).map(one => ({ by: one.author.includes("@") ? `${one.author} (by email)` : one.author, text: one.body })),
|
|
67
|
+
history: store.flowEvents(card.id).slice(-8).map(one => `${one.outcome} → ${title(one.toStage)}${one.note === null ? "" : `: ${one.note}`}`),
|
|
68
|
+
},
|
|
69
|
+
asked: question !== null && question.state === "answered" ? [{ question: question.question, answer: [question.choice === null ? null : question.options.find(one => one.id === question.choice)?.label ?? question.choice, question.answer].filter(Boolean).join(" — ") }] : [],
|
|
70
|
+
notes: memory.told.map(one => ({ by: one.createdBy, text: one.text })),
|
|
71
|
+
memory: memory.kept.map(one => one.text),
|
|
72
|
+
tools, toolsSpent: spent,
|
|
73
|
+
calls: store.teammateCallsOn(card.id, card.entry).map(call => {
|
|
74
|
+
const question = call.state === "denied" ? store.teammateQuestionForCall(call.id) : null;
|
|
75
|
+
return { name: callName(call.tool, call.action), input: JSON.stringify(call.input), outcome: callOutcome(call),
|
|
76
|
+
said: call.state === "denied" ? question?.answer ?? null : call.state === "asked" ? null : call.result };
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Run one teammate turn on a card and carry out what it decided. With tools
|
|
82
|
+
* (v94) a turn is a short loop: each tool call it asks for is made (or put to
|
|
83
|
+
* its person, and the turn waits), and it decides again with the answer —
|
|
84
|
+
* until it decides, or it has used its tools as much as a visit allows.
|
|
85
|
+
*/
|
|
86
|
+
export async function teammateTurn(store, flow, definition, stage, card, mate, now, io) {
|
|
87
|
+
const model = teammateModel(store, mate);
|
|
88
|
+
if (store.teammateGrants(mate.id).length > 0) {
|
|
89
|
+
try {
|
|
90
|
+
await refreshGrants(store, mate, now, io);
|
|
91
|
+
}
|
|
92
|
+
catch { /* the last listing stands */ }
|
|
93
|
+
// Calls a person approved since the last turn are made first, exactly as they approved them.
|
|
94
|
+
for (const call of store.teammateCallsOn(card.id, card.entry).filter(one => one.state === "approved" && one.undoOf === null))
|
|
95
|
+
await makeCall(store, call, flow.repo, io, now);
|
|
96
|
+
}
|
|
97
|
+
const log = [];
|
|
98
|
+
for (let turn = 1;; turn++) {
|
|
99
|
+
const made = store.teammateCallsOn(card.id, card.entry).length;
|
|
100
|
+
const spent = made >= CALLS_PER_VISIT || turn > TURNS_PER_RUN;
|
|
101
|
+
const tools = spent ? [] : offeredTools(store, mate);
|
|
102
|
+
const context = contextOf(store, flow, definition, stage, card, mate, tools, spent && made > 0);
|
|
103
|
+
const asked = Date.now();
|
|
104
|
+
const reply = await (io.turn ?? claudeTurnRunner())({ model, prompt: turnPrompt(context), timeoutMs: TURN_TIMEOUT_MS });
|
|
105
|
+
// v97: every turn is kept with what it cost, for its weekly report.
|
|
106
|
+
store.addTeammateTurn({ teammate: mate.id, card: card.id, model, ok: reply.ok, ms: reply.ok ? reply.ms : Date.now() - asked, costUsd: reply.costUsd ?? null, tokensIn: reply.tokensIn ?? null, tokensOut: reply.tokensOut ?? null }, now);
|
|
107
|
+
if (!reply.ok)
|
|
108
|
+
return { state: "retry", said: reply.said, ...(log.length === 0 ? {} : { log: log.join("\n\n") }) };
|
|
109
|
+
const answer = readTurn(reply.value, context);
|
|
110
|
+
if (answer === null)
|
|
111
|
+
return { state: "retry", said: `${context.name}'s answer wasn't one this zone allows.`, log: [...log, JSON.stringify(reply.value).slice(0, 4000)].join("\n\n") };
|
|
112
|
+
const header = `${context.name} (${model}) · ${(reply.ms / 1000).toFixed(1)} s`;
|
|
113
|
+
if (answer.remember !== "")
|
|
114
|
+
remember(store, mate, answer.remember, { source: "teammate", card: card.id, by: teammateActor({ name: context.name }) }, now);
|
|
115
|
+
if (answer.action !== "use_tool") {
|
|
116
|
+
const outcome = carryOut(store, flow, definition, stage, card, mate, context, answer, now, io, header);
|
|
117
|
+
return log.length === 0 ? outcome : { ...outcome, log: `${log.join("\n\n")}\n\n${outcome.log ?? ""}`.trim() };
|
|
118
|
+
}
|
|
119
|
+
log.push(`${header}\n\n${JSON.stringify(answer)}`);
|
|
120
|
+
const waiting = await useTool(store, flow, stage, card, mate, context, answer, io, now);
|
|
121
|
+
if (waiting !== null)
|
|
122
|
+
return { ...waiting, log: log.join("\n\n") };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* One tool call a turn asked for, checked against its rules: made now (the
|
|
127
|
+
* turn goes on: null), refused with the reason (the turn goes on and reads
|
|
128
|
+
* it), or put to its person — the turn waits for the answer.
|
|
129
|
+
*/
|
|
130
|
+
async function useTool(store, flow, stage, card, mate, context, answer, io, now) {
|
|
131
|
+
const split = answer.tool.indexOf(".");
|
|
132
|
+
const tool = split < 0 ? answer.tool : answer.tool.slice(0, split), action = split < 0 ? "" : answer.tool.slice(split + 1);
|
|
133
|
+
const receipt = { teammate: mate.id, card: card.id, entry: card.entry, tool: tool.slice(0, 40), action: action.slice(0, 64) || "(none)", why: answer.reason };
|
|
134
|
+
const refuse = (input, said) => { store.addTeammateCall({ ...receipt, input, rule: "never", state: "refused", result: said }, now); return null; };
|
|
135
|
+
if (!(context.tools ?? []).some(one => one.name === answer.tool))
|
|
136
|
+
return refuse({}, `There's no ${answer.tool} among the tools you may use.`);
|
|
137
|
+
let input;
|
|
138
|
+
try {
|
|
139
|
+
const parsed = JSON.parse(answer.input === "" ? "{}" : answer.input);
|
|
140
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
141
|
+
return refuse({}, "Its input wasn't a JSON object.");
|
|
142
|
+
input = parsed;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return refuse({}, "Its input wasn't a JSON object.");
|
|
146
|
+
}
|
|
147
|
+
const grant = store.teammateGrant(mate.id, tool);
|
|
148
|
+
const info = grant.actions.find(one => one.name === action);
|
|
149
|
+
const problem = inputProblem(info, input);
|
|
150
|
+
if (problem !== null)
|
|
151
|
+
return refuse(input, problem);
|
|
152
|
+
const rule = ruleFor(grant, action, input);
|
|
153
|
+
if (rule.use === "never")
|
|
154
|
+
return refuse(input, rule.why);
|
|
155
|
+
if (rule.use === "free") {
|
|
156
|
+
const id = store.addTeammateCall({ ...receipt, input, rule: "free", state: "running" }, now);
|
|
157
|
+
await makeCall(store, store.teammateCall(id), flow.repo, io, now);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
// Ask first: its person approves this exact call, in their chat app or on the card.
|
|
161
|
+
const soul = { name: context.name, role: context.role };
|
|
162
|
+
const id = store.addTeammateCall({ ...receipt, input, rule: "ask", state: "asked", result: rule.why }, now);
|
|
163
|
+
const words = callWords(tool, action, input, 480);
|
|
164
|
+
const question = store.openTeammateQuestion({ teammate: mate.id, card: card.id, entry: card.entry, question: `Use ${words}?`, options: [{ id: APPROVE, label: "Approve" }, { id: DENY, label: "Deny" }], askedOf: context.person, toolCall: id }, now);
|
|
165
|
+
if (question === null)
|
|
166
|
+
return { state: "failed", said: "It asked to make the same call twice." };
|
|
167
|
+
notifyPeople(store, card, [context.person], null, { key: `teammate-q:${question}`, attention: true, subject: `${teammateLabel(soul)} asks to use ${action} on “${card.title}”`,
|
|
168
|
+
body: `${words}\n\n${answer.reason === "" ? "" : `Why: ${answer.reason}\n`}(${rule.why})\n\nApprove makes exactly this call. Deny stops it. Or answer in words to tell ${context.name} what to do instead.`,
|
|
169
|
+
}, now);
|
|
170
|
+
if (stage.kind !== "approval")
|
|
171
|
+
store.updateFlowCard(card.id, { waiting: `${context.name} asked ${context.person} to approve: ${words}`.slice(0, 300) }, now);
|
|
172
|
+
return { state: "waiting", said: `Asked ${context.person} to approve ${action}`, nextAt: ASKED };
|
|
173
|
+
}
|
|
174
|
+
function carryOut(store, flow, definition, stage, card, mate, context, answer, now, io, header) {
|
|
175
|
+
const soul = { name: context.name, role: context.role };
|
|
176
|
+
const actor = teammateActor(soul), label = teammateLabel(soul);
|
|
177
|
+
const decisionJson = JSON.stringify(answer);
|
|
178
|
+
const log = `${header}\n\n${decisionJson}`;
|
|
179
|
+
const event = (kind, said) => store.addTeammateEvent({ teammate: mate.id, card: card.id, entry: card.entry, kind, said, detail: { flow: flow.id, zone: stage.id, action: answer.action, reason: answer.reason } }, now);
|
|
180
|
+
const titleOf = (id) => definition.stages.find(one => one.id === id)?.title ?? "the next zone";
|
|
181
|
+
const repos = [flow.repo];
|
|
182
|
+
if (answer.action === "approve" || answer.action === "send_back") {
|
|
183
|
+
const draft = draftFor(definition, stage);
|
|
184
|
+
const decided = decideFlowCard(store, {
|
|
185
|
+
card: card.id, decision: answer.action === "approve" ? "approve" : "send-back", note: answer.action === "approve" ? answer.reason || null : answer.note, actor, repos, teammate: mate.handle, entry: card.entry,
|
|
186
|
+
...(answer.action === "approve" && draft !== null && answer.text !== "" ? { draft: keptDraft(answer.text) } : {}),
|
|
187
|
+
...(io.evidenceRoot === undefined ? {} : { evidenceRoot: io.evidenceRoot }),
|
|
188
|
+
}, now);
|
|
189
|
+
if (!decided.ok)
|
|
190
|
+
return { state: "failed", said: decided.message, log, decisionJson };
|
|
191
|
+
const said = answer.action === "approve" ? `Approved “${card.title}”${answer.reason === "" ? "" : `: ${answer.reason}`}` : `Sent “${card.title}” back: ${answer.note}`;
|
|
192
|
+
event("decided", said);
|
|
193
|
+
return { state: "passed", said, log, decisionJson };
|
|
194
|
+
}
|
|
195
|
+
if (answer.action === "hand_off") {
|
|
196
|
+
event("handed", `Handed “${card.title}” to ${context.person}${answer.note === "" ? "" : `: ${answer.note}`}`);
|
|
197
|
+
// The engine sees the hand-off (a passed turn in the same visit) and asks the person, with this note.
|
|
198
|
+
return { state: "passed", said: `Handed to ${context.person}`, log, decisionJson };
|
|
199
|
+
}
|
|
200
|
+
if (answer.action === "ask") {
|
|
201
|
+
const options = answer.options.map((label, at) => ({ id: `o${at + 1}`, label }));
|
|
202
|
+
const opened = store.openTeammateQuestion({ teammate: mate.id, card: card.id, entry: card.entry, question: answer.question, options, askedOf: context.person }, now);
|
|
203
|
+
if (opened === null)
|
|
204
|
+
return { state: "failed", said: "It asked twice about the same visit.", log, decisionJson };
|
|
205
|
+
event("asked", `Asked ${context.person} about “${card.title}”: ${answer.question}`);
|
|
206
|
+
notifyPeople(store, card, [context.person], null, { key: `teammate-q:${opened}`, attention: true, subject: `${label} asks about “${card.title}”`,
|
|
207
|
+
body: `${answer.question}${options.length === 0 ? "" : `\n\n${options.map(one => `• ${one.label}`).join("\n")}`}${answer.reason === "" ? "" : `\n\n(${answer.reason})`}\n\nAnswer with the buttons in your chat app, or on the card.`,
|
|
208
|
+
}, now);
|
|
209
|
+
store.updateFlowCard(card.id, { waiting: `${context.name} asked ${context.person}: ${answer.question}`.slice(0, 300) }, now);
|
|
210
|
+
return { state: "waiting", said: `Asked ${context.person}`, log, decisionJson, nextAt: ASKED };
|
|
211
|
+
}
|
|
212
|
+
if (answer.action === "cant") {
|
|
213
|
+
event("handled", `Couldn't handle “${card.title}”: ${answer.note}`);
|
|
214
|
+
if (stage.onFail !== null)
|
|
215
|
+
store.moveFlowCard(card.id, { to: stage.onFail, outcome: "fail", actor, note: answer.note || null, expectEntry: card.entry }, now);
|
|
216
|
+
else
|
|
217
|
+
store.updateFlowCard(card.id, { waiting: `${context.name} couldn't handle it: ${answer.note}`.slice(0, 300) }, now);
|
|
218
|
+
return { state: "failed", said: answer.note || "It couldn't handle this card.", log, decisionJson };
|
|
219
|
+
}
|
|
220
|
+
// route: the answer names where the card goes; its text is what the next zones use.
|
|
221
|
+
const picked = handleAnswers(stage).find(one => one.answer.toLowerCase() === answer.answer.toLowerCase());
|
|
222
|
+
if (answer.text !== "")
|
|
223
|
+
store.updateFlowCard(card.id, { outputs: { ...card.outputs, [stage.id]: keptDraft(answer.text) } }, now);
|
|
224
|
+
const said = `Sent “${card.title}” to ${titleOf(picked.to)}${picked.answer === CARRY_ON ? "" : ` (${picked.answer})`}${answer.reason === "" ? "" : `: ${answer.reason}`}`;
|
|
225
|
+
event("handled", said);
|
|
226
|
+
// v96: a zone that answers whoever asked sends what it wrote back to them, under its name.
|
|
227
|
+
if (stage.reply === true && answer.text !== "")
|
|
228
|
+
replyToAsker(store, flow, card, mate, answer.text, now);
|
|
229
|
+
if (picked.to === null)
|
|
230
|
+
store.updateFlowCard(card.id, { waiting: "Finished here. Move the card on when you're ready." }, now);
|
|
231
|
+
else
|
|
232
|
+
store.moveFlowCard(card.id, { to: picked.to, outcome: "ok", actor, historyNote: `${actor}: ${picked.answer === CARRY_ON ? "" : `${picked.answer}. `}${answer.reason}`.trim(), expectEntry: card.entry }, now);
|
|
233
|
+
return { state: "passed", said, log, decisionJson };
|
|
234
|
+
}
|
|
235
|
+
/** A person answers a teammate's question: once, by the person it was asked of; the card's turn is due again. */
|
|
236
|
+
export function answerTeammateQuestion(store, id, answer, now) {
|
|
237
|
+
return store.transact(() => {
|
|
238
|
+
const question = store.teammateQuestion(id);
|
|
239
|
+
if (question === null || question.state !== "open")
|
|
240
|
+
return { ok: false, said: "That question was already answered." };
|
|
241
|
+
if (question.askedOf !== answer.by)
|
|
242
|
+
return { ok: false, said: `Only ${question.askedOf} can answer this one.` };
|
|
243
|
+
const choice = answer.choice === null ? null : question.options.find(one => one.id === answer.choice) ?? null;
|
|
244
|
+
const text = answer.text?.trim().slice(0, 2000) || null;
|
|
245
|
+
if (choice === null && text === null)
|
|
246
|
+
return { ok: false, said: "Pick an answer or write one." };
|
|
247
|
+
const card = store.getFlowCard(question.card);
|
|
248
|
+
// v95: a rule change it suggested to its manager. Nothing about the card waits on it.
|
|
249
|
+
if (question.suggestion !== null) {
|
|
250
|
+
const mate = store.getTeammate(question.teammate);
|
|
251
|
+
const decided = mate === null ? { ok: false, said: "That teammate is off the team." } : answerSuggestion(store, mate, question.suggestion, { choice: choice?.id ?? null, text, by: answer.by }, now);
|
|
252
|
+
store.answerTeammateQuestion(id, { choice: choice?.id ?? null, text, by: answer.by, via: answer.via }, now);
|
|
253
|
+
store.addTeammateEvent({ teammate: question.teammate, card: question.card, entry: question.entry, kind: "answered", said: `${answer.by}: ${[choice?.label, text].filter(Boolean).join(" — ")}`, by: answer.by }, now);
|
|
254
|
+
return decided;
|
|
255
|
+
}
|
|
256
|
+
// v94: an ask-first tool call. Approve makes exactly that call (on the card's next turn); Deny, or words alone, don't.
|
|
257
|
+
if (question.toolCall !== null) {
|
|
258
|
+
if (card === null || card.state !== "active" || card.entry !== question.entry) {
|
|
259
|
+
store.moveTeammateCall(question.toolCall, ["asked"], { state: "refused", result: "The card moved on before anyone approved it." }, now);
|
|
260
|
+
store.dropTeammateQuestion(id, now);
|
|
261
|
+
return { ok: false, said: "That card has moved on, so nothing was done." };
|
|
262
|
+
}
|
|
263
|
+
if (!store.moveTeammateCall(question.toolCall, ["asked"], { state: choice?.id === APPROVE ? "approved" : "denied", decidedBy: answer.by }, now))
|
|
264
|
+
return { ok: false, said: "That call was already decided." };
|
|
265
|
+
// v95: approving the same action again and again teaches it to suggest a looser rule.
|
|
266
|
+
const mate = store.getTeammate(question.teammate), call = store.teammateCall(question.toolCall);
|
|
267
|
+
if (choice?.id === APPROVE && mate !== null && call !== null)
|
|
268
|
+
considerSuggestion(store, mate, call, now);
|
|
269
|
+
}
|
|
270
|
+
store.answerTeammateQuestion(id, { choice: choice?.id ?? null, text, by: answer.by, via: answer.via }, now);
|
|
271
|
+
store.addTeammateEvent({ teammate: question.teammate, card: question.card, entry: question.entry, kind: "answered", said: `${answer.by}: ${[choice?.label, text].filter(Boolean).join(" — ")}`, by: answer.by }, now);
|
|
272
|
+
if (card !== null && card.state === "active" && card.entry === question.entry) {
|
|
273
|
+
store.updateFlowCard(card.id, { waiting: null }, now);
|
|
274
|
+
store.wakeFlowStep(card.id, card.entry, now);
|
|
275
|
+
}
|
|
276
|
+
return { ok: true, said: question.toolCall === null ? "Answered. It picks the card up again now." : choice?.id === APPROVE ? "Approved. The call is made now, exactly as shown." : "It won't make that call. It picks the card up again now." };
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/** A card's link, for messages about it. */
|
|
280
|
+
export const teammateCardHref = (flow, card) => flowCardHref(flow, card);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Teammates pages (v92): who is on the team, and one page per teammate —
|
|
3
|
+
* what it asks you, its soul file, what it did and why, what you told it,
|
|
4
|
+
* and its settings. Server-rendered forms, like the other settings pages.
|
|
5
|
+
*/
|
|
6
|
+
import type { Store, TeammateRow } from "./store.js";
|
|
7
|
+
/** v94: the Tools section's rule rows; a limit's fields show only while "Up to a limit" is chosen. */
|
|
8
|
+
export declare const TEAMMATE_CSS = ".teammate-tools .tool-grant{border-top:1px solid var(--so-line);padding-top:12px;margin-top:12px}.teammate-tools .tool-grant:first-of-type{border-top:0;margin-top:0;padding-top:0}.teammate-tools h3{font-size:1rem;margin:0 0 4px}.teammate-tools .tool-rule{display:grid;gap:6px;padding:10px 0;border-bottom:1px solid var(--so-line)}.teammate-tools .tool-rule:last-of-type{border-bottom:0}.teammate-tools .tool-rule .meta{display:block}.teammate-tools select,.teammate-tools input{max-width:100%;box-sizing:border-box}.teammate-tools .tool-limit{display:none;flex-wrap:wrap;align-items:center;gap:6px}.teammate-tools .tool-rule:has(option[value=\"limit\"]:checked) .tool-limit{display:flex}.teammate-tools .tool-rule>select{width:auto;min-width:16em}.teammate-tools .tool-limit select{width:auto}.teammate-tools .tool-limit input{width:7em}.teammate-tools .tool-buttons{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.teammate-tools button{min-height:44px}.teammate-activity li{overflow-wrap:anywhere}.teammate-memory .memory-tell,.teammate-memory .memory-search{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:0 0 12px}.teammate-memory .memory-tell textarea{flex:1 1 280px}.teammate-memory .memory-search input{flex:1 1 220px}.teammate-memory button{min-height:44px}.teammate-memories{list-style:none;padding:0;margin:0}.teammate-memories li{padding:10px 0;border-top:1px solid var(--so-line);overflow-wrap:anywhere}.teammate-memories li:first-child{border-top:0}.teammate-memories .meta{display:block;font-size:.85rem}.teammate-memories details.memory-edit{border:0;padding:0;margin:2px 0 0;background:transparent;box-shadow:none}.memory-edit summary{cursor:pointer;min-height:32px;display:inline-flex;align-items:center;font-size:.85rem;font-weight:400;color:var(--so-muted)}.tool-undo{border:0;padding:0;margin:8px 0;background:transparent;box-shadow:none}.tool-undo summary{cursor:pointer;min-height:36px;display:inline-flex;align-items:center;font-size:.9rem}.tool-undo-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:6px 0}.tool-undo-row select{width:auto}.teammate-overrides,.teammate-undo{padding-left:18px}.teammate-undo li{margin:6px 0}.teammate-undo form{display:inline}.teammate-week h3{font-size:1rem;margin:14px 0 4px}.teammate-week button{min-height:44px}.teammate-routines{list-style:none;padding:0;margin:8px 0}.teammate-routines li{display:grid;gap:4px;padding:10px 0;border-top:1px solid var(--so-line)}.teammate-routines li:first-child{border-top:0}.routine-buttons{display:flex;flex-wrap:wrap;gap:8px}.routine-buttons button,.routine-add button{min-height:44px}.routine-add{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:8px 0}.routine-add label{display:grid;gap:4px;flex:1 1 200px}.routine-add label:first-of-type{flex:0 1 180px}.teammate-actions,.question-options{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0}.teammate-actions form,.question-options form{margin:0}.memory-edit form{display:grid;gap:8px;margin-top:6px}.memory-buttons{display:flex;flex-wrap:wrap;gap:8px}@media(max-width:600px){.teammate-tools select,.teammate-tools input{font-size:16px}}";
|
|
9
|
+
/** A blank soul file: the sections a teammate reads, to fill in. */
|
|
10
|
+
export declare const BLANK_SOUL = "---\nname: \nrole: \n---\n\n## Who you are\n\n\n## How you write\n\n\n## What you know\n\n\n## Decide on your own\n- \n\n## Ask first\n- \n\n## Never\n- \n";
|
|
11
|
+
export declare function teammatesListHtml(store: Store, mates: readonly TeammateRow[], projects: readonly string[], projectName: (repo: string) => string, csrf: string, canCreate: boolean, notice: {
|
|
12
|
+
said?: string | null;
|
|
13
|
+
problem?: string | null;
|
|
14
|
+
}): string;
|
|
15
|
+
export declare function teammatePageHtml(store: Store, mate: TeammateRow, viewer: string, projectName: (repo: string) => string, csrf: string, canManage: boolean, approvers: readonly string[], notice: {
|
|
16
|
+
said?: string | null;
|
|
17
|
+
problem?: string | null;
|
|
18
|
+
soulDraft?: string | null;
|
|
19
|
+
query?: string | null;
|
|
20
|
+
}): string;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { labelOf, nameOf, summaryOf, TEAMMATE_MODELS, zonesOf } from "./teammate-admin.js";
|
|
2
|
+
import { TEAMMATE_TEMPLATES } from "./teammates.js";
|
|
3
|
+
import { projectToolsOf } from "./project-tools.js";
|
|
4
|
+
import { callWords, defaultRule, numberFields, receiptWords } from "./teammate-tools.js";
|
|
5
|
+
import { MEMORY_CHARS, searchMemories } from "./teammate-memory.js";
|
|
6
|
+
import { deskOf, localZone, routinesOf } from "./teammate-desk.js";
|
|
7
|
+
import { weekOf, weekWords } from "./teammate-week.js";
|
|
8
|
+
import { describeSchedule, parseSchedule } from "./routine.js";
|
|
9
|
+
/** v94: the Tools section's rule rows; a limit's fields show only while "Up to a limit" is chosen. */
|
|
10
|
+
export const TEAMMATE_CSS = `.teammate-tools .tool-grant{border-top:1px solid var(--so-line);padding-top:12px;margin-top:12px}.teammate-tools .tool-grant:first-of-type{border-top:0;margin-top:0;padding-top:0}.teammate-tools h3{font-size:1rem;margin:0 0 4px}.teammate-tools .tool-rule{display:grid;gap:6px;padding:10px 0;border-bottom:1px solid var(--so-line)}.teammate-tools .tool-rule:last-of-type{border-bottom:0}.teammate-tools .tool-rule .meta{display:block}.teammate-tools select,.teammate-tools input{max-width:100%;box-sizing:border-box}.teammate-tools .tool-limit{display:none;flex-wrap:wrap;align-items:center;gap:6px}.teammate-tools .tool-rule:has(option[value="limit"]:checked) .tool-limit{display:flex}.teammate-tools .tool-rule>select{width:auto;min-width:16em}.teammate-tools .tool-limit select{width:auto}.teammate-tools .tool-limit input{width:7em}.teammate-tools .tool-buttons{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.teammate-tools button{min-height:44px}.teammate-activity li{overflow-wrap:anywhere}.teammate-memory .memory-tell,.teammate-memory .memory-search{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:0 0 12px}.teammate-memory .memory-tell textarea{flex:1 1 280px}.teammate-memory .memory-search input{flex:1 1 220px}.teammate-memory button{min-height:44px}.teammate-memories{list-style:none;padding:0;margin:0}.teammate-memories li{padding:10px 0;border-top:1px solid var(--so-line);overflow-wrap:anywhere}.teammate-memories li:first-child{border-top:0}.teammate-memories .meta{display:block;font-size:.85rem}.teammate-memories details.memory-edit{border:0;padding:0;margin:2px 0 0;background:transparent;box-shadow:none}.memory-edit summary{cursor:pointer;min-height:32px;display:inline-flex;align-items:center;font-size:.85rem;font-weight:400;color:var(--so-muted)}.tool-undo{border:0;padding:0;margin:8px 0;background:transparent;box-shadow:none}.tool-undo summary{cursor:pointer;min-height:36px;display:inline-flex;align-items:center;font-size:.9rem}.tool-undo-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:6px 0}.tool-undo-row select{width:auto}.teammate-overrides,.teammate-undo{padding-left:18px}.teammate-undo li{margin:6px 0}.teammate-undo form{display:inline}.teammate-week h3{font-size:1rem;margin:14px 0 4px}.teammate-week button{min-height:44px}.teammate-routines{list-style:none;padding:0;margin:8px 0}.teammate-routines li{display:grid;gap:4px;padding:10px 0;border-top:1px solid var(--so-line)}.teammate-routines li:first-child{border-top:0}.routine-buttons{display:flex;flex-wrap:wrap;gap:8px}.routine-buttons button,.routine-add button{min-height:44px}.routine-add{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:8px 0}.routine-add label{display:grid;gap:4px;flex:1 1 200px}.routine-add label:first-of-type{flex:0 1 180px}.teammate-actions,.question-options{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0}.teammate-actions form,.question-options form{margin:0}.memory-edit form{display:grid;gap:8px;margin-top:6px}.memory-buttons{display:flex;flex-wrap:wrap;gap:8px}@media(max-width:600px){.teammate-tools select,.teammate-tools input{font-size:16px}}`;
|
|
11
|
+
const USES = [["free", "Do it"], ["limit", "Do it, up to a limit"], ["ask", "Ask first"], ["never", "Never"]];
|
|
12
|
+
/** v94: which project tools it may use, and its rule for each action. */
|
|
13
|
+
function toolsSection(store, mate, csrf, canManage) {
|
|
14
|
+
const name = nameOf(mate);
|
|
15
|
+
const tools = projectToolsOf(store, mate.repo);
|
|
16
|
+
const grants = store.teammateGrants(mate.id);
|
|
17
|
+
const toolsPage = `/settings/tools?repo=${encodeURIComponent(mate.repo)}`;
|
|
18
|
+
const granted = grants.map(grant => {
|
|
19
|
+
const gone = !tools.some(one => one.name === grant.tool);
|
|
20
|
+
const rows = grant.actions.map(action => {
|
|
21
|
+
const rule = grant.rules[action.name] ?? defaultRule(action);
|
|
22
|
+
const use = rule.use === "free" && rule.limit !== undefined ? "limit" : rule.use;
|
|
23
|
+
const numbers = numberFields(action);
|
|
24
|
+
const words = rule.use === "never" ? "Never" : rule.use === "ask" ? "Ask first" : rule.limit === undefined ? "Do it" : `Do it up to ${rule.limit.field} ${rule.limit.over}, ask first above`;
|
|
25
|
+
if (!canManage)
|
|
26
|
+
return `<li class="tool-rule"><span><strong>${e(action.name)}</strong> · ${e(words)}</span>${action.about === "" ? "" : `<span class="meta">${e(action.about)}</span>`}</li>`;
|
|
27
|
+
const field = `${grant.tool}-${action.name}`.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
28
|
+
return `<div class="tool-rule" data-tool-action="${e(action.name)}"><label for="use-${e(field)}"><strong>${e(action.name)}</strong>${action.about === "" ? "" : `<span class="meta">${e(action.about)}</span>`}</label>` +
|
|
29
|
+
`<select id="use-${e(field)}" name="use.${e(action.name)}">${USES.filter(([value]) => value !== "limit" || numbers.length > 0).map(([value, label]) => `<option value="${value}"${value === use ? " selected" : ""}>${label}</option>`).join("")}</select>` +
|
|
30
|
+
(numbers.length === 0 ? "" : `<span class="tool-limit">Ask first when <select name="field.${e(action.name)}" aria-label="The number the limit is on">${numbers.map(one => `<option value="${e(one)}"${rule.limit?.field === one ? " selected" : ""}>${e(one)}</option>`).join("")}</select> is over <input type="number" name="over.${e(action.name)}" min="0" step="any" value="${rule.limit === undefined ? "" : rule.limit.over}" aria-label="The limit"></span>`) +
|
|
31
|
+
`</div>`;
|
|
32
|
+
}).join("");
|
|
33
|
+
const head = `<h3>${e(grant.tool)}</h3>${gone ? `<p class="problem">This project doesn't have ${e(grant.tool)} any more, so ${e(name)} can't use it.</p>` : ""}`;
|
|
34
|
+
// v97: which action undoes which, for the Undo button on its receipts. Optional, so it's folded away.
|
|
35
|
+
const undoing = grant.actions.filter(one => !one.readOnly).map(action => {
|
|
36
|
+
const chosen = grant.rules[action.name]?.undo ?? "";
|
|
37
|
+
return `<label class="tool-undo-row">${e(action.name)} is undone by<select name="undo.${e(action.name)}"><option value="">nothing</option>${grant.actions.filter(other => other.name !== action.name).map(other => `<option value="${e(other.name)}"${other.name === chosen ? " selected" : ""}>${e(other.name)}</option>`).join("")}</select></label>`;
|
|
38
|
+
}).join("");
|
|
39
|
+
const undoBox = undoing === "" ? "" : `<details class="tool-undo"${grant.actions.some(one => grant.rules[one.name]?.undo !== undefined) ? " open" : ""}><summary>Undo</summary><p class="meta">When an action has an opposite, a person can press Undo on its receipts: the opposite is called with the same input.</p>${undoing}</details>`;
|
|
40
|
+
return canManage
|
|
41
|
+
? `<div class="tool-grant" data-tool-grant="${e(grant.tool)}">${head}<form method="post" action="/teammates/${mate.id}/tools">${hidden(csrf)}<input type="hidden" name="tool" value="${e(grant.tool)}">${rows}` +
|
|
42
|
+
undoBox + `<div class="tool-buttons"><button name="op" value="rules">Save rules</button><button name="op" value="revoke" class="secondary">Stop using ${e(grant.tool)}</button></div></form></div>`
|
|
43
|
+
: `<div class="tool-grant" data-tool-grant="${e(grant.tool)}">${head}<ul>${rows}</ul></div>`;
|
|
44
|
+
}).join("");
|
|
45
|
+
const open = tools.filter(one => !grants.some(grant => grant.tool === one.name));
|
|
46
|
+
const add = !canManage ? "" : tools.length === 0 ? `<p class="meta">This project has no tools yet. <a href="${e(toolsPage)}">Add one</a>, then let ${e(name)} use it here.</p>`
|
|
47
|
+
: open.length === 0 ? "" : `<form method="post" action="/teammates/${mate.id}/tools" class="tool-add">${hidden(csrf)}<input type="hidden" name="op" value="grant"><label>Let ${e(name)} use<select name="tool">${open.map(one => `<option value="${e(one.name)}">${e(one.name)}${one.spec.about === "" ? "" : ` — ${e(one.spec.about.slice(0, 80))}`}</option>`).join("")}</select></label><button>Add tool</button></form>`;
|
|
48
|
+
if (grants.length === 0 && add === "")
|
|
49
|
+
return "";
|
|
50
|
+
return `<section class="card teammate-tools" id="tools"><h2>Tools</h2>${grants.length === 0 ? `<p class="meta">${e(name)} doesn't use any tools yet. Reading starts as “Do it”; everything else asks you first.</p>` : ""}${granted}${add}</section>`;
|
|
51
|
+
}
|
|
52
|
+
const e = (text) => text.replace(/[&<>"']/g, one => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[one]);
|
|
53
|
+
const when = (at) => new Date(at).toLocaleString("en", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
|
|
54
|
+
const hidden = (csrf) => `<input type="hidden" name="csrf" value="${e(csrf)}">`;
|
|
55
|
+
const startOfDay = () => { const day = new Date(); day.setHours(0, 0, 0, 0); return day.toISOString(); };
|
|
56
|
+
/** A blank soul file: the sections a teammate reads, to fill in. */
|
|
57
|
+
export const BLANK_SOUL = `---\nname: \nrole: \n---\n\n## Who you are\n\n\n## How you write\n\n\n## What you know\n\n\n## Decide on your own\n- \n\n## Ask first\n- \n\n## Never\n- \n`;
|
|
58
|
+
export function teammatesListHtml(store, mates, projects, projectName, csrf, canCreate, notice) {
|
|
59
|
+
const rows = mates.map(mate => {
|
|
60
|
+
const today = store.teammateTurnsSince(mate.id, startOfDay());
|
|
61
|
+
const open = store.openTeammateQuestions([mate.id]).length;
|
|
62
|
+
return `<article class="card" data-teammate="${e(mate.handle)}"><div class="flow-row"><h2><a href="/teammates/${mate.id}">${e(labelOf(mate))}</a></h2><span class="flow-counts">${e(projectName(mate.repo))} · ${mate.state === "active" ? "Working" : "Paused"} · ${today} today${open > 0 ? ` · ${open} question${open === 1 ? "" : "s"} for you` : ""}</span></div></article>`;
|
|
63
|
+
}).join("");
|
|
64
|
+
const create = canCreate && projects.length > 0
|
|
65
|
+
? `<details class="card"${mates.length === 0 ? " open" : ""}><summary>New teammate</summary><form method="post" action="/teammates/new" data-new-teammate>${hidden(csrf)}` +
|
|
66
|
+
`<label>Start from<select name="template">${TEAMMATE_TEMPLATES.map(one => `<option value="${e(one.id)}">${e(one.label)}: ${e(one.about)}</option>`).join("")}<option value="blank">Blank: write its soul file yourself</option></select></label>` +
|
|
67
|
+
`<label>Name<input name="name" maxlength="40" placeholder="keep the template's name"></label>` +
|
|
68
|
+
(projects.length === 1 ? `<input type="hidden" name="repo" value="${e(projects[0])}">` : `<label>Project<select name="repo">${projects.map(repo => `<option value="${e(repo)}">${e(projectName(repo))}</option>`).join("")}</select></label>`) +
|
|
69
|
+
`<button>Add teammate</button></form></details>`
|
|
70
|
+
: "";
|
|
71
|
+
const intro = `<p class="meta">AI teammates work your flows' cards within rules you write: they decide, reply and move cards on, and ask you when their rules say to. The quickest start is a <a href="/kits">starter kit</a>: a teammate and its flow, set up in one click.</p>`;
|
|
72
|
+
return `<section class="teammates">${notice.problem ? `<p class="problem" role="alert">${e(notice.problem)}</p>` : ""}${notice.said ? `<p class="said" role="status">${e(notice.said)}</p>` : ""}${intro}${rows || '<p class="meta">No teammates yet.</p>'}${create}</section>`;
|
|
73
|
+
}
|
|
74
|
+
/** v97: its week: what it did and cost, what people overrode, and calls that can be undone. */
|
|
75
|
+
function weekSection(store, mate, csrf, canManage) {
|
|
76
|
+
const week = weekOf(store, mate, new Date());
|
|
77
|
+
const overrides = week.overrides.length === 0 ? "" : `<h3>What you overrode</h3><ul class="teammate-overrides">${week.overrides.map(one => `<li>${e(one.said)}${one.href === null ? "" : ` · <a href="${e(one.href)}">open</a>`} <span class="meta">${when(one.at)}</span></li>`).join("")}</ul>`;
|
|
78
|
+
const undo = week.undoable.length === 0 ? "" : `<h3>Undo</h3><ul class="teammate-undo">${week.undoable.map(one => `<li data-undo="${one.call}"><span>${e(one.words)}</span> <span class="meta">${when(one.at)}${one.href === null ? "" : ` · <a href="${e(one.href)}">open</a>`}</span>` +
|
|
79
|
+
(canManage ? `<form method="post" action="/teammates/${mate.id}/week">${hidden(csrf)}<input type="hidden" name="id" value="${one.call}"><button name="op" value="undo" class="secondary">Undo with ${e(one.undo)}</button></form>` : "") + `</li>`).join("")}</ul>`;
|
|
80
|
+
const send = canManage ? `<form method="post" action="/teammates/${mate.id}/week">${hidden(csrf)}<button name="op" value="send" class="secondary">Send the week's report</button></form>` : "";
|
|
81
|
+
return `<details class="card teammate-week" id="week" open><summary>This week</summary><p class="teammate-week-said">${e(weekWords(mate, week)).replace(/\n/g, "<br>")}</p>${overrides}${undo}${send}</details>`;
|
|
82
|
+
}
|
|
83
|
+
/** v96: its desk — where messages to it by name and its routines land — and its routines. */
|
|
84
|
+
function deskSection(store, mate, csrf, canManage) {
|
|
85
|
+
const name = nameOf(mate);
|
|
86
|
+
const desk = deskOf(store, mate);
|
|
87
|
+
const routines = routinesOf(store, mate);
|
|
88
|
+
const intro = `<p class="meta">Message ${e(name)} by name in your chat app, like “@${e(mate.handle)} where's order 1042?”. It lands on ${desk === null ? "its desk" : `<a href="/flows/${desk.id}">${e(name)}'s desk</a>`}, and the answer comes back to you.</p>`;
|
|
89
|
+
const list = routines.length === 0 ? "" : `<ul class="teammate-routines">${routines.map(one => `<li data-routine="${one.id}"><span><strong>${e(scheduleWords(one.schedule))}</strong> · ${e(one.text)}</span>` +
|
|
90
|
+
`<span class="meta">${one.state === "paused" ? "Paused" : one.nextAt === null ? "" : `Next ${when(one.nextAt)}`}${one.lastAt === null ? "" : ` · last ${when(one.lastAt)}${one.lastOutcome === null ? "" : `: ${e(one.lastOutcome)}`}`}</span>` +
|
|
91
|
+
(canManage ? `<form method="post" action="/teammates/${mate.id}/routines" class="routine-buttons">${hidden(csrf)}<input type="hidden" name="id" value="${one.id}"><button name="op" value="run" class="secondary">Run now</button><button name="op" value="remove" class="secondary">Remove</button></form>` : "") + `</li>`).join("")}</ul>`;
|
|
92
|
+
const add = canManage ? `<form method="post" action="/teammates/${mate.id}/routines" class="routine-add">${hidden(csrf)}<input type="hidden" name="op" value="add">` +
|
|
93
|
+
`<label>When<input type="text" name="schedule" maxlength="80" placeholder="weekdays 09:00" required></label><label>What to do<input type="text" name="text" maxlength="200" placeholder="Look up yesterday's refunds and tell me the total." required></label><button>Add routine</button></form>` : "";
|
|
94
|
+
return `<section class="card teammate-desk" id="desk"><h2>Desk and routines</h2>${intro}${list}${add}${canManage ? `<p class="meta">A routine puts a card on its desk on that schedule (“weekdays 09:00”, “daily 17:00”, “monday 09:00”, “every 2 hours”; times are this computer's, ${e(localZone())}, unless you name one); the answer goes to ${e(mate.manager)}.</p>` : ""}</section>`;
|
|
95
|
+
}
|
|
96
|
+
/** A schedule's stored form ("weekdays:09:00@Europe/London") in words. */
|
|
97
|
+
const scheduleWords = (text) => { const parsed = parseSchedule(text); return parsed === null ? text : describeSchedule(parsed); };
|
|
98
|
+
/** v95: what it remembers — what its people told it, and what it kept from cards — to search, edit and forget. */
|
|
99
|
+
function memorySection(store, mate, csrf, canManage, query) {
|
|
100
|
+
const name = nameOf(mate);
|
|
101
|
+
const all = store.teammateMemories(mate.id, { limit: 500 });
|
|
102
|
+
const shown = query === null || query.trim() === "" ? all.slice(0, 30) : searchMemories(store, mate, query);
|
|
103
|
+
const tell = canManage ? `<form method="post" action="/teammates/${mate.id}/note" class="memory-tell">${hidden(csrf)}<label class="sr-only" for="teammate-note">Tell ${e(name)} something</label><textarea id="teammate-note" name="note" rows="2" maxlength="${MEMORY_CHARS}" placeholder="For example: this week, offer free shipping instead of a refund when you can."></textarea><button>Tell ${e(name)}</button></form>` : "";
|
|
104
|
+
const search = all.length > 8 || (query ?? "") !== "" ? `<form method="get" action="/teammates/${mate.id}#memory" class="memory-search" role="search"><label class="sr-only" for="memory-q">Search what ${e(name)} remembers</label><input id="memory-q" type="search" name="q" value="${e(query ?? "")}" placeholder="Search what ${e(name)} remembers"><button class="secondary">Search</button></form>` : "";
|
|
105
|
+
const items = shown.map(one => {
|
|
106
|
+
const card = one.card === null ? null : store.getFlowCard(one.card);
|
|
107
|
+
const whence = one.source === "person" ? `${e(one.createdBy)} told it` : card === null ? "it kept this" : `it kept this from <a href="/flows/${card.flow}?card=${card.id}">${e(card.title.slice(0, 60))}</a>`;
|
|
108
|
+
const edit = canManage ? `<details class="memory-edit"><summary>Edit</summary><form method="post" action="/teammates/${mate.id}/memory">${hidden(csrf)}<input type="hidden" name="id" value="${one.id}"><label class="sr-only" for="memory-${one.id}">Memory</label><textarea id="memory-${one.id}" name="text" rows="2" maxlength="${MEMORY_CHARS}">${e(one.text)}</textarea><div class="memory-buttons"><button name="op" value="edit">Save</button><button name="op" value="forget" class="secondary">Forget</button></div></form></details>` : "";
|
|
109
|
+
return `<li data-memory="${one.id}" data-source="${one.source}"><span>${e(one.text)}</span> <span class="meta">${whence} · ${when(one.updatedAt)}</span>${edit}</li>`;
|
|
110
|
+
}).join("");
|
|
111
|
+
const empty = (query ?? "") !== "" ? `<p class="meta">Nothing ${e(name)} remembers matches that.</p>` : `<p class="meta">Nothing yet. What you tell ${e(name)} is kept here, and it keeps short facts from the cards it works.</p>`;
|
|
112
|
+
return `<section class="card teammate-memory" id="memory"><h2>Memory</h2>${tell}${search}${shown.length === 0 ? empty : `<ul class="teammate-memories">${items}</ul>`}${query === null && all.length > shown.length ? `<p class="meta">${all.length - shown.length} older ${all.length - shown.length === 1 ? "memory" : "memories"}: search to find them.</p>` : ""}</section>`;
|
|
113
|
+
}
|
|
114
|
+
export function teammatePageHtml(store, mate, viewer, projectName, csrf, canManage, approvers, notice) {
|
|
115
|
+
const name = nameOf(mate);
|
|
116
|
+
const questions = store.openTeammateQuestions([mate.id]);
|
|
117
|
+
const asked = questions.map(question => {
|
|
118
|
+
const card = store.getFlowCard(question.card);
|
|
119
|
+
const flow = card === null ? null : store.getFlow(card.flow);
|
|
120
|
+
const mine = question.askedOf === viewer;
|
|
121
|
+
const cardLink = card === null || flow === null ? "a card" : `<a href="/flows/${flow.id}?card=${card.id}">${e(card.title)}</a>`;
|
|
122
|
+
return `<article class="card teammate-question" data-question="${question.id}"${question.suggestion === null ? "" : " data-suggestion"}><p class="meta">${question.suggestion === null ? `About ${cardLink}` : "Suggests a rule change"} · ${when(question.createdAt)}${mine ? "" : ` · for ${e(question.askedOf)}`}</p><p><strong>${e(question.question)}</strong></p>` +
|
|
123
|
+
(mine ? `<div class="question-options">${question.options.map(one => `<form method="post" action="/teammates/questions/${question.id}/answer">${hidden(csrf)}<input type="hidden" name="choice" value="${e(one.id)}"><button>${e(one.label)}</button></form>`).join("")}</div>` +
|
|
124
|
+
`<form method="post" action="/teammates/questions/${question.id}/answer" class="question-reply">${hidden(csrf)}<label>Or answer in your words<textarea name="text" rows="2" maxlength="2000"></textarea></label><button>Answer</button></form>` : "") +
|
|
125
|
+
`</article>`;
|
|
126
|
+
}).join("");
|
|
127
|
+
const zones = zonesOf(store, mate);
|
|
128
|
+
const where = zones.length === 0
|
|
129
|
+
? `<p class="meta">Not on any zone yet. In a flow, choose ${e(name)} under “Who decides” on a “Person decides” zone, or add a “Teammate handles it” zone.</p>`
|
|
130
|
+
: `<ul>${zones.map(one => `<li><a href="/flows/${one.flow}">${e(one.flowName)}</a> · ${one.kind === "decides" ? "decides" : "handles"} ${e(one.title)}</li>`).join("")}</ul>`;
|
|
131
|
+
// What it did and why, with every tool call it made or asked to make (v94), newest first.
|
|
132
|
+
const events = [
|
|
133
|
+
...store.teammateEvents(mate.id, 40).filter(one => one.kind !== "note").map(one => ({ kind: one.kind, said: one.said, card: one.card, at: one.at })),
|
|
134
|
+
...store.teammateCallsOf(mate.id, 40).map(one => ({ kind: "call", said: `Used ${callWords(one.tool, one.action, one.input, 200)} · ${receiptWords(store, one)}`, card: one.card, at: one.createdAt })),
|
|
135
|
+
].sort((a, b) => b.at.localeCompare(a.at)).slice(0, 40);
|
|
136
|
+
const activity = events.length === 0 ? `<p class="meta">Nothing yet.</p>` : `<ul class="teammate-activity">${events.map(one => {
|
|
137
|
+
const card = one.card === null ? null : store.getFlowCard(one.card);
|
|
138
|
+
return `<li data-event="${one.kind}">${e(one.said)}${card === null ? "" : ` · <a href="/flows/${card.flow}?card=${card.id}">open</a>`} <span class="meta">${when(one.at)}</span></li>`;
|
|
139
|
+
}).join("")}</ul>`;
|
|
140
|
+
const versions = store.teammateVersions(mate.id);
|
|
141
|
+
const latest = versions[0];
|
|
142
|
+
const manage = canManage ? `<div class="teammate-actions">` +
|
|
143
|
+
`<form method="post" action="/teammates/${mate.id}/state">${hidden(csrf)}<input type="hidden" name="state" value="${mate.state === "active" ? "paused" : "active"}"><button${mate.state === "active" ? ' class="secondary"' : ""}>${mate.state === "active" ? `Pause ${e(name)}` : `Resume ${e(name)}`}</button></form>` +
|
|
144
|
+
`<form method="post" action="/teammates/${mate.id}/summary">${hidden(csrf)}<button class="secondary">Send today's summary</button></form></div>` : "";
|
|
145
|
+
return [
|
|
146
|
+
notice.problem ? `<p class="problem" role="alert">${e(notice.problem)}</p>` : "",
|
|
147
|
+
notice.said ? `<p class="said" role="status">${e(notice.said)}</p>` : "",
|
|
148
|
+
`<p class="meta">${e(projectName(mate.repo))} · ${mate.state === "active" ? "Working" : "Paused: its decisions go to people"} · reports to ${e(mate.manager)}</p>`,
|
|
149
|
+
manage,
|
|
150
|
+
questions.length === 0 ? "" : `<section><h2>Questions</h2>${asked}</section>`,
|
|
151
|
+
`<section class="card"><h2>Today</h2><p class="teammate-summary">${e(summaryOf(store, mate, startOfDay()).said).replace(/\n/g, "<br>")}</p></section>`,
|
|
152
|
+
weekSection(store, mate, csrf, canManage),
|
|
153
|
+
`<section class="card"><h2>Works on</h2>${where}</section>`,
|
|
154
|
+
toolsSection(store, mate, csrf, canManage),
|
|
155
|
+
deskSection(store, mate, csrf, canManage),
|
|
156
|
+
memorySection(store, mate, csrf, canManage, notice.query ?? null),
|
|
157
|
+
`<details class="card"${canManage ? " open" : ""}><summary>Soul file</summary><p class="meta">Who ${e(name)} is and its rules, read every turn. Version ${mate.version}${latest === undefined ? "" : ` · saved by ${e(latest.savedBy)}, ${when(latest.savedAt)}`} · <a href="/teammates/${mate.id}/soul.md" download>Download</a></p>` +
|
|
158
|
+
(canManage ? `<form method="post" action="/teammates/${mate.id}/soul" data-soul-form>${hidden(csrf)}<label class="sr-only" for="teammate-soul">Soul file</label><textarea id="teammate-soul" name="soul" rows="24" class="mono" maxlength="12000" spellcheck="false">${e(notice.soulDraft ?? mate.soul)}</textarea><button>Save</button></form>` : `<pre class="mono">${e(mate.soul)}</pre>`) + `</details>`,
|
|
159
|
+
`<details class="card"><summary>What ${e(name)} did</summary>${activity}</details>`,
|
|
160
|
+
canManage ? `<details class="card"><summary>Settings</summary><form method="post" action="/teammates/${mate.id}/settings">${hidden(csrf)}` +
|
|
161
|
+
`<label>Model<select name="model">${TEAMMATE_MODELS.map(one => `<option value="${one}"${(mate.model ?? "default") === one ? " selected" : ""}>${one === "default" ? "The lead chat's model" : one[0].toUpperCase() + one.slice(1)}</option>`).join("")}</select></label>` +
|
|
162
|
+
`<label>Turns a day, at most<input type="number" name="dailyTurns" min="1" max="2000" value="${mate.dailyTurns}"></label>` +
|
|
163
|
+
`<label>Reports to<select name="manager">${[...new Set([mate.manager, ...approvers])].map(one => `<option value="${e(one)}"${one === mate.manager ? " selected" : ""}>${e(one)}</option>`).join("")}</select></label><button>Save settings</button></form>` +
|
|
164
|
+
`<form method="post" action="/teammates/${mate.id}/state" class="danger-zone">${hidden(csrf)}<input type="hidden" name="state" value="removed"><button class="danger">Remove ${e(name)} from the team</button></form></details>` : "",
|
|
165
|
+
].join("");
|
|
166
|
+
}
|