codeply-cli 0.5.0 → 0.5.2
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/THIRD_PARTY_NOTICES.md +65 -63
- package/bin/codeply.js +1 -1
- package/lib/agent.mjs +133 -24
- package/lib/ai.js +268 -9
- package/lib/ai.mjs +6 -6
- package/lib/apply-patch.js +217 -217
- package/lib/arity-table.json +138 -138
- package/lib/arity.js +70 -70
- package/lib/auth.mjs +6 -6
- package/lib/bots.js +493 -16
- package/lib/chatgpt.js +282 -0
- package/lib/cloud-browser.mjs +143 -143
- package/lib/cloud.mjs +1186 -1105
- package/lib/commands.js +103 -103
- package/lib/config.js +83 -3
- package/lib/design-library/assemble-artifact.mjs +21 -21
- package/lib/design-library/cleanup.mjs +45 -45
- package/lib/design-library/index.json +22379 -22379
- package/lib/design-library/make-table-data.mjs +39 -39
- package/lib/diagnostics.js +192 -192
- package/lib/edit-engine.js +364 -364
- package/lib/github-agent.mjs +311 -311
- package/lib/history.js +75 -75
- package/lib/mail-watch.js +504 -0
- package/lib/mcp.js +258 -258
- package/lib/model-sync.js +114 -0
- package/lib/native-tools.mjs +16 -1
- package/lib/oauth-connectors.js +146 -24
- package/lib/permissions.js +109 -109
- package/lib/plugins.js +353 -353
- package/lib/publish.js +916 -0
- package/lib/reach.js +67 -0
- package/lib/server.mjs +457 -457
- package/lib/session-db.js +220 -220
- package/lib/share.js +168 -168
- package/lib/skills.js +2 -0
- package/lib/snapshot.js +146 -146
- package/lib/telemetry.js +102 -0
- package/lib/terminal-hints.js +154 -154
- package/lib/tools.mjs +316 -14
- package/lib/typecheck-worker.js +147 -147
- package/lib/web-tools.js +209 -158
- package/package.json +49 -49
- package/skills/SOURCE.md +58 -58
- package/skills/accessibility/SKILL.md +147 -147
- package/skills/agent-architecture-audit/SKILL.md +257 -257
- package/skills/agent-eval/SKILL.md +147 -147
- package/skills/agent-harness-construction/SKILL.md +74 -74
- package/skills/agent-introspection-debugging/SKILL.md +154 -154
- package/skills/agent-payment-x402/SKILL.md +225 -225
- package/skills/agent-self-evaluation/SKILL.md +182 -182
- package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -71
- package/skills/agent-self-evaluation/references/hook-integration.md +64 -64
- package/skills/agent-sort/SKILL.md +216 -216
- package/skills/agentic-engineering/SKILL.md +64 -64
- package/skills/agentic-os/SKILL.md +388 -388
- package/skills/ai-first-engineering/SKILL.md +52 -52
- package/skills/ai-regression-testing/SKILL.md +386 -386
- package/skills/android-clean-architecture/SKILL.md +340 -340
- package/skills/angular-developer/SKILL.md +155 -155
- package/skills/angular-developer/references/angular-animations.md +160 -160
- package/skills/angular-developer/references/angular-aria.md +410 -410
- package/skills/angular-developer/references/cli.md +86 -86
- package/skills/angular-developer/references/component-harnesses.md +59 -59
- package/skills/angular-developer/references/component-styling.md +91 -91
- package/skills/angular-developer/references/components.md +117 -117
- package/skills/angular-developer/references/creating-services.md +97 -97
- package/skills/angular-developer/references/data-resolvers.md +69 -69
- package/skills/angular-developer/references/define-routes.md +67 -67
- package/skills/angular-developer/references/defining-providers.md +72 -72
- package/skills/angular-developer/references/di-fundamentals.md +120 -120
- package/skills/angular-developer/references/e2e-testing.md +56 -56
- package/skills/angular-developer/references/effects.md +83 -83
- package/skills/angular-developer/references/hierarchical-injectors.md +43 -43
- package/skills/angular-developer/references/host-elements.md +80 -80
- package/skills/angular-developer/references/injection-context.md +63 -63
- package/skills/angular-developer/references/inputs.md +101 -101
- package/skills/angular-developer/references/linked-signal.md +59 -59
- package/skills/angular-developer/references/loading-strategies.md +61 -61
- package/skills/angular-developer/references/mcp.md +108 -108
- package/skills/angular-developer/references/navigate-to-routes.md +69 -69
- package/skills/angular-developer/references/outputs.md +86 -86
- package/skills/angular-developer/references/reactive-forms.md +122 -122
- package/skills/angular-developer/references/rendering-strategies.md +44 -44
- package/skills/angular-developer/references/resource.md +77 -77
- package/skills/angular-developer/references/route-animations.md +56 -56
- package/skills/angular-developer/references/route-guards.md +52 -52
- package/skills/angular-developer/references/router-lifecycle.md +45 -45
- package/skills/angular-developer/references/router-testing.md +87 -87
- package/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
- package/skills/angular-developer/references/signal-forms.md +795 -795
- package/skills/angular-developer/references/signals-overview.md +94 -94
- package/skills/angular-developer/references/tailwind-css.md +69 -69
- package/skills/angular-developer/references/template-driven-forms.md +114 -114
- package/skills/angular-developer/references/testing-fundamentals.md +65 -65
- package/skills/api-connector-builder/SKILL.md +121 -121
- package/skills/api-design/SKILL.md +524 -524
- package/skills/architecture-decision-records/SKILL.md +180 -180
- package/skills/article-writing/SKILL.md +80 -80
- package/skills/automation-audit-ops/SKILL.md +143 -143
- package/skills/autonomous-agent-harness/SKILL.md +274 -274
- package/skills/autonomous-loops/SKILL.md +611 -611
- package/skills/backend-patterns/SKILL.md +562 -562
- package/skills/benchmark/SKILL.md +95 -95
- package/skills/benchmark-methodology/SKILL.md +191 -191
- package/skills/benchmark-optimization-loop/SKILL.md +71 -71
- package/skills/blender-motion-state-inspection/SKILL.md +165 -165
- package/skills/blueprint/SKILL.md +106 -106
- package/skills/brand-discovery/SKILL.md +145 -145
- package/skills/brand-discovery/references/10_purpose-why.md +40 -40
- package/skills/brand-discovery/references/20_positioning.md +44 -44
- package/skills/brand-discovery/references/30_audience-niche.md +52 -52
- package/skills/brand-discovery/references/40_personality-archetype.md +57 -57
- package/skills/brand-discovery/references/50_voice-tone.md +59 -59
- package/skills/brand-discovery/references/60_narrative-story.md +50 -50
- package/skills/brand-discovery/references/70_founder-tension.md +49 -49
- package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -133
- package/skills/brand-voice/SKILL.md +98 -98
- package/skills/brand-voice/references/voice-profile-schema.md +55 -55
- package/skills/browser-qa/SKILL.md +105 -105
- package/skills/bun-runtime/SKILL.md +85 -85
- package/skills/canary-watch/SKILL.md +108 -108
- package/skills/carrier-relationship-management/SKILL.md +212 -212
- package/skills/cisco-ios-patterns/SKILL.md +164 -164
- package/skills/ck/SKILL.md +148 -148
- package/skills/claude-devfleet/SKILL.md +112 -112
- package/skills/click-path-audit/SKILL.md +245 -245
- package/skills/clickhouse-io/SKILL.md +445 -445
- package/skills/code-tour/SKILL.md +254 -254
- package/skills/codebase-onboarding/SKILL.md +234 -234
- package/skills/codehealth-mcp/SKILL.md +167 -167
- package/skills/coding-standards/SKILL.md +551 -551
- package/skills/competitive-platform-analysis/SKILL.md +214 -214
- package/skills/competitive-report-structure/SKILL.md +162 -162
- package/skills/compose-multiplatform-patterns/SKILL.md +300 -300
- package/skills/config-gc/SKILL.md +120 -120
- package/skills/configure-ecc/SKILL.md +385 -385
- package/skills/connections-optimizer/SKILL.md +190 -190
- package/skills/content-engine/SKILL.md +132 -132
- package/skills/content-hash-cache-pattern/SKILL.md +162 -162
- package/skills/context-budget/SKILL.md +136 -136
- package/skills/continuous-agent-loop/SKILL.md +46 -46
- package/skills/continuous-learning/SKILL.md +132 -132
- package/skills/continuous-learning-v2/SKILL.md +361 -361
- package/skills/contract-first/SKILL.md +287 -287
- package/skills/cost-aware-llm-pipeline/SKILL.md +184 -184
- package/skills/cost-tracking/SKILL.md +97 -97
- package/skills/council/SKILL.md +204 -204
- package/skills/cpp-coding-standards/SKILL.md +724 -724
- package/skills/cpp-testing/SKILL.md +325 -325
- package/skills/crosspost/SKILL.md +112 -112
- package/skills/csharp-testing/SKILL.md +322 -322
- package/skills/customer-billing-ops/SKILL.md +141 -141
- package/skills/customs-trade-compliance/SKILL.md +263 -263
- package/skills/dart-flutter-patterns/SKILL.md +564 -564
- package/skills/dashboard-builder/SKILL.md +109 -109
- package/skills/data-scraper-agent/SKILL.md +765 -765
- package/skills/data-throughput-accelerator/SKILL.md +74 -74
- package/skills/database-migrations/SKILL.md +430 -430
- package/skills/deep-research/SKILL.md +160 -160
- package/skills/defi-amm-security/SKILL.md +167 -167
- package/skills/delivery-gate/SKILL.md +126 -126
- package/skills/deployment-patterns/SKILL.md +428 -428
- package/skills/design-system/SKILL.md +83 -83
- package/skills/django-celery/SKILL.md +458 -458
- package/skills/django-patterns/SKILL.md +735 -735
- package/skills/django-security/SKILL.md +644 -644
- package/skills/django-tdd/SKILL.md +730 -730
- package/skills/django-verification/SKILL.md +470 -470
- package/skills/dmux-workflows/SKILL.md +192 -192
- package/skills/docker-patterns/SKILL.md +365 -365
- package/skills/documentation-lookup/SKILL.md +91 -91
- package/skills/dotnet-patterns/SKILL.md +322 -322
- package/skills/dynamic-workflow-mode/SKILL.md +124 -124
- package/skills/e2e-testing/SKILL.md +327 -327
- package/skills/ecc-guide/SKILL.md +190 -190
- package/skills/ecc-recipes/SKILL.md +149 -149
- package/skills/ecc-tools-cost-audit/SKILL.md +161 -161
- package/skills/email-ops/SKILL.md +122 -122
- package/skills/energy-procurement/SKILL.md +228 -228
- package/skills/enterprise-agent-ops/SKILL.md +51 -51
- package/skills/error-handling/SKILL.md +377 -377
- package/skills/eval-harness/SKILL.md +271 -271
- package/skills/evm-token-decimals/SKILL.md +131 -131
- package/skills/exa-search/SKILL.md +108 -108
- package/skills/fal-ai-media/SKILL.md +289 -289
- package/skills/fastapi-patterns/SKILL.md +514 -514
- package/skills/finance-billing-ops/SKILL.md +128 -128
- package/skills/flox-environments/SKILL.md +497 -497
- package/skills/flutter-dart-code-review/SKILL.md +436 -436
- package/skills/foundation-models-on-device/SKILL.md +243 -243
- package/skills/frontend-a11y/SKILL.md +446 -446
- package/skills/frontend-design-direction/SKILL.md +93 -93
- package/skills/frontend-patterns/SKILL.md +657 -657
- package/skills/frontend-slides/SKILL.md +185 -185
- package/skills/fsharp-testing/SKILL.md +281 -281
- package/skills/gan-style-harness/SKILL.md +279 -279
- package/skills/gateguard/SKILL.md +133 -133
- package/skills/generating-python-installer/SKILL.md +820 -820
- package/skills/git-workflow/SKILL.md +716 -716
- package/skills/github-ops/SKILL.md +145 -145
- package/skills/golang-patterns/SKILL.md +676 -676
- package/skills/golang-testing/SKILL.md +721 -721
- package/skills/google-workspace-ops/SKILL.md +96 -96
- package/skills/growth-log/SKILL.md +128 -128
- package/skills/healthcare-cdss-patterns/SKILL.md +246 -246
- package/skills/healthcare-emr-patterns/SKILL.md +160 -160
- package/skills/healthcare-eval-harness/SKILL.md +208 -208
- package/skills/healthcare-phi-compliance/SKILL.md +146 -146
- package/skills/hermes-imports/SKILL.md +89 -89
- package/skills/hexagonal-architecture/SKILL.md +277 -277
- package/skills/hipaa-compliance/SKILL.md +79 -79
- package/skills/homelab-network-readiness/SKILL.md +170 -170
- package/skills/homelab-network-setup/SKILL.md +130 -130
- package/skills/homelab-pihole-dns/SKILL.md +275 -275
- package/skills/homelab-vlan-segmentation/SKILL.md +312 -312
- package/skills/homelab-wireguard-vpn/SKILL.md +306 -306
- package/skills/hookify-rules/SKILL.md +128 -128
- package/skills/inherit-legacy-style/SKILL.md +157 -157
- package/skills/intent-driven-development/SKILL.md +360 -360
- package/skills/inventory-demand-planning/SKILL.md +247 -247
- package/skills/investor-materials/SKILL.md +97 -97
- package/skills/investor-outreach/SKILL.md +92 -92
- package/skills/ios-icon-gen/SKILL.md +158 -158
- package/skills/iterative-retrieval/SKILL.md +212 -212
- package/skills/ito-basket-compare/SKILL.md +64 -64
- package/skills/ito-compute/SKILL.md +129 -129
- package/skills/ito-data-atlas-agent/SKILL.md +64 -64
- package/skills/ito-market-intelligence/SKILL.md +61 -61
- package/skills/ito-trade-planner/SKILL.md +68 -68
- package/skills/java-coding-standards/SKILL.md +384 -384
- package/skills/jira-integration/SKILL.md +303 -303
- package/skills/jpa-patterns/SKILL.md +152 -152
- package/skills/knowledge-ops/SKILL.md +155 -155
- package/skills/kotlin-coroutines-flows/SKILL.md +285 -285
- package/skills/kotlin-exposed-patterns/SKILL.md +720 -720
- package/skills/kotlin-ktor-patterns/SKILL.md +690 -690
- package/skills/kotlin-patterns/SKILL.md +712 -712
- package/skills/kotlin-testing/SKILL.md +825 -825
- package/skills/kubernetes-patterns/SKILL.md +756 -756
- package/skills/laravel-patterns/SKILL.md +416 -416
- package/skills/laravel-plugin-discovery/SKILL.md +230 -230
- package/skills/laravel-security/SKILL.md +948 -948
- package/skills/laravel-tdd/SKILL.md +675 -675
- package/skills/laravel-verification/SKILL.md +180 -180
- package/skills/latency-critical-systems/SKILL.md +75 -75
- package/skills/lead-intelligence/SKILL.md +322 -322
- package/skills/liquid-glass-design/SKILL.md +279 -279
- package/skills/llm-trading-agent-security/SKILL.md +147 -147
- package/skills/logistics-exception-management/SKILL.md +222 -222
- package/skills/loop-design-check/SKILL.md +143 -143
- package/skills/mailtrap-email-integration/SKILL.md +77 -77
- package/skills/make-interfaces-feel-better/SKILL.md +152 -152
- package/skills/manim-video/SKILL.md +90 -90
- package/skills/market-research/SKILL.md +76 -76
- package/skills/marketing-campaign/SKILL.md +114 -114
- package/skills/mcp-server-patterns/SKILL.md +70 -70
- package/skills/messages-ops/SKILL.md +105 -105
- package/skills/ml-adoption-playbook/SKILL.md +57 -57
- package/skills/mle-workflow/SKILL.md +348 -348
- package/skills/motion-advanced/SKILL.md +596 -596
- package/skills/motion-foundations/SKILL.md +299 -299
- package/skills/motion-patterns/SKILL.md +434 -434
- package/skills/motion-ui/SKILL.md +576 -576
- package/skills/mysql-patterns/SKILL.md +413 -413
- package/skills/nanoclaw-repl/SKILL.md +34 -34
- package/skills/nestjs-patterns/SKILL.md +231 -231
- package/skills/netmiko-ssh-automation/SKILL.md +174 -174
- package/skills/network-bgp-diagnostics/SKILL.md +168 -168
- package/skills/network-config-validation/SKILL.md +211 -211
- package/skills/network-interface-health/SKILL.md +153 -153
- package/skills/nextjs-turbopack/SKILL.md +58 -58
- package/skills/nodejs-keccak256/SKILL.md +103 -103
- package/skills/nutrient-document-processing/SKILL.md +168 -168
- package/skills/nuxt4-patterns/SKILL.md +101 -101
- package/skills/openclaw-persona-forge/SKILL.md +289 -289
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -53
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -39
- package/skills/openclaw-persona-forge/references/output-template.md +166 -166
- package/skills/opensource-pipeline/SKILL.md +256 -256
- package/skills/orch-add-feature/SKILL.md +45 -45
- package/skills/orch-build-mvp/SKILL.md +49 -49
- package/skills/orch-change-feature/SKILL.md +43 -43
- package/skills/orch-fix-defect/SKILL.md +43 -43
- package/skills/orch-pipeline/SKILL.md +121 -121
- package/skills/orch-refine-code/SKILL.md +44 -44
- package/skills/parallel-execution-optimizer/SKILL.md +74 -74
- package/skills/perl-patterns/SKILL.md +505 -505
- package/skills/perl-security/SKILL.md +504 -504
- package/skills/perl-testing/SKILL.md +476 -476
- package/skills/plan-canvas/SKILL.md +153 -153
- package/skills/plan-orchestrate/SKILL.md +263 -263
- package/skills/plankton-code-quality/SKILL.md +237 -237
- package/skills/postgres-patterns/SKILL.md +148 -148
- package/skills/prediction-market-oracle-research/SKILL.md +64 -64
- package/skills/prediction-market-risk-review/SKILL.md +61 -61
- package/skills/premium-web-design/SKILL.md +154 -154
- package/skills/prisma-patterns/SKILL.md +401 -401
- package/skills/product-capability/SKILL.md +142 -142
- package/skills/product-lens/SKILL.md +93 -93
- package/skills/production-audit/SKILL.md +207 -207
- package/skills/production-scheduling/SKILL.md +238 -238
- package/skills/project-flow-ops/SKILL.md +112 -112
- package/skills/prompt-optimizer/SKILL.md +398 -398
- package/skills/publish-website/SKILL.md +76 -0
- package/skills/python-patterns/SKILL.md +751 -751
- package/skills/python-testing/SKILL.md +817 -817
- package/skills/pytorch-patterns/SKILL.md +397 -397
- package/skills/quality-nonconformance/SKILL.md +260 -260
- package/skills/quarkus-patterns/SKILL.md +723 -723
- package/skills/quarkus-security/SKILL.md +468 -468
- package/skills/quarkus-tdd/SKILL.md +812 -812
- package/skills/quarkus-verification/SKILL.md +480 -480
- package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -68
- package/skills/react-native-patterns/SKILL.md +326 -326
- package/skills/react-patterns/SKILL.md +342 -342
- package/skills/react-performance/SKILL.md +575 -575
- package/skills/react-testing/SKILL.md +424 -424
- package/skills/recsys-pipeline-architect/SKILL.md +115 -115
- package/skills/recursive-decision-ledger/SKILL.md +81 -81
- package/skills/redis-patterns/SKILL.md +404 -404
- package/skills/regex-vs-llm-structured-text/SKILL.md +221 -221
- package/skills/remotion-video-creation/SKILL.md +43 -43
- package/skills/repo-scan/SKILL.md +79 -79
- package/skills/research-ops/SKILL.md +113 -113
- package/skills/returns-reverse-logistics/SKILL.md +240 -240
- package/skills/rules-distill/SKILL.md +265 -265
- package/skills/rust-patterns/SKILL.md +500 -500
- package/skills/rust-testing/SKILL.md +501 -501
- package/skills/safety-guard/SKILL.md +76 -76
- package/skills/santa-method/SKILL.md +307 -307
- package/skills/scientific-db-pubmed-database/SKILL.md +176 -176
- package/skills/scientific-db-uspto-database/SKILL.md +178 -178
- package/skills/scientific-pkg-gget/SKILL.md +167 -167
- package/skills/scientific-thinking-literature-review/SKILL.md +193 -193
- package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -161
- package/skills/search-first/SKILL.md +183 -183
- package/skills/security-bounty-hunter/SKILL.md +100 -100
- package/skills/security-review/SKILL.md +504 -504
- package/skills/security-scan/SKILL.md +166 -166
- package/skills/seo/SKILL.md +155 -155
- package/skills/skill-comply/SKILL.md +59 -59
- package/skills/skill-scout/SKILL.md +141 -141
- package/skills/skill-stocktake/SKILL.md +195 -195
- package/skills/social-graph-ranker/SKILL.md +155 -155
- package/skills/social-publisher/SKILL.md +130 -130
- package/skills/springboot-patterns/SKILL.md +315 -315
- package/skills/springboot-security/SKILL.md +273 -273
- package/skills/springboot-tdd/SKILL.md +159 -159
- package/skills/springboot-verification/SKILL.md +232 -232
- package/skills/strategic-compact/SKILL.md +142 -142
- package/skills/swift-actor-persistence/SKILL.md +144 -144
- package/skills/swift-concurrency-6-2/SKILL.md +216 -216
- package/skills/swift-protocol-di-testing/SKILL.md +191 -191
- package/skills/swiftui-patterns/SKILL.md +259 -259
- package/skills/taste/SKILL.md +264 -264
- package/skills/taste/references/genre-taxonomy.md +87 -87
- package/skills/tdd-workflow/SKILL.md +583 -583
- package/skills/team-agent-orchestration/SKILL.md +111 -111
- package/skills/team-builder/SKILL.md +169 -169
- package/skills/terminal-ops/SKILL.md +110 -110
- package/skills/tinystruct-patterns/SKILL.md +279 -279
- package/skills/tinystruct-patterns/references/architecture.md +90 -90
- package/skills/tinystruct-patterns/references/data-handling.md +60 -60
- package/skills/tinystruct-patterns/references/database.md +99 -99
- package/skills/tinystruct-patterns/references/routing.md +64 -64
- package/skills/tinystruct-patterns/references/system-usage.md +97 -97
- package/skills/tinystruct-patterns/references/testing.md +72 -72
- package/skills/token-budget-advisor/SKILL.md +134 -134
- package/skills/ui-demo/SKILL.md +466 -466
- package/skills/ui-to-vue/SKILL.md +135 -135
- package/skills/uncloud/SKILL.md +344 -344
- package/skills/unified-memory/SKILL.md +170 -170
- package/skills/unified-notifications-ops/SKILL.md +188 -188
- package/skills/verification-loop/SKILL.md +129 -129
- package/skills/video-editing/SKILL.md +311 -311
- package/skills/videodb/SKILL.md +375 -375
- package/skills/visa-doc-translate/SKILL.md +117 -117
- package/skills/vite-patterns/SKILL.md +450 -450
- package/skills/vue-patterns/SKILL.md +471 -471
- package/skills/windows-desktop-e2e/SKILL.md +888 -888
- package/skills/workspace-surface-audit/SKILL.md +126 -126
- package/skills/x-api/SKILL.md +235 -235
package/lib/bots.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* for a chat, buildBotPrompt() is added to that run's system prompt. After
|
|
7
7
|
* each reply, learnFromTurn() asks the model for at most 3 durable facts to
|
|
8
8
|
* remember, so the bot gets better at working with this user over time.
|
|
9
|
+
* After a run that used tools, reflectOnRun() looks at what the bot did and
|
|
10
|
+
* keeps experience (lessons, playbooks, tool tips, open threads), so the bot
|
|
11
|
+
* also gets better at the work itself.
|
|
9
12
|
*
|
|
10
13
|
* Bots talk to each other through the ask_bot tool. delegate() runs the asked
|
|
11
14
|
* bot as an isolated sub-run (its own prompt, only the task text it was
|
|
@@ -23,6 +26,12 @@ const path = require('path');
|
|
|
23
26
|
const MAX_MEMORY = 60;
|
|
24
27
|
const MAX_FACTS_PER_TURN = 3;
|
|
25
28
|
const MAX_DEPTH = 2;
|
|
29
|
+
// Experience memory (MUSE style: learned from the bot's own work, not about the user).
|
|
30
|
+
const MAX_LESSONS = 12;
|
|
31
|
+
const MAX_PLAYBOOKS = 40;
|
|
32
|
+
const MAX_TOOL_TIPS = 15;
|
|
33
|
+
const MAX_OPEN_THREADS = 5;
|
|
34
|
+
const PLAYBOOKS_IN_FULL = 2;
|
|
26
35
|
|
|
27
36
|
let dirOverride = null;
|
|
28
37
|
function botsDir() {
|
|
@@ -49,10 +58,44 @@ const APPROVALS = {
|
|
|
49
58
|
edit_files: { label: 'Editing files', tools: ['write_file', 'edit_file', 'apply_patch', 'fetch_image'] },
|
|
50
59
|
run_commands: { label: 'Running commands', tools: ['run'] },
|
|
51
60
|
send: { label: 'Sending anything (email, Slack)', tools: ['gmail_send', 'slack_post_message'] },
|
|
52
|
-
publish: { label: 'Deploying or publishing', tools: ['vercel_deploy', 'github_create_repo', 'vercel_api'] },
|
|
53
|
-
databases: { label: 'Changing databases or cloud projects', tools: ['supabase_create_project', 'supabase_delete_project', 'supabase_api', 'supabase_sql'] },
|
|
61
|
+
publish: { label: 'Deploying or publishing', tools: ['vercel_deploy', 'github_create_repo', 'vercel_api', 'publish_deploy', 'publish_github'] },
|
|
62
|
+
databases: { label: 'Changing databases or cloud projects', tools: ['supabase_create_project', 'supabase_delete_project', 'supabase_api', 'supabase_sql', 'supabase_setup', 'supabase_schema'] },
|
|
63
|
+
calendar: { label: 'Adding to your calendar', tools: ['calendar_add'] },
|
|
54
64
|
};
|
|
55
65
|
const ALL_APPROVALS = Object.keys(APPROVALS);
|
|
66
|
+
// The categories bots were saved with before `calendar` existed. A bot saved
|
|
67
|
+
// back then never chose about the newer ones, so it asks for them (listBots).
|
|
68
|
+
const FIRST_APPROVALS = ['edit_files', 'run_commands', 'send', 'publish', 'databases'];
|
|
69
|
+
|
|
70
|
+
// Always on (mail-watch.js): the bot watches in the background, with no model
|
|
71
|
+
// calls until something important arrives. What it can watch, and how it
|
|
72
|
+
// reaches the user when it finds something.
|
|
73
|
+
const WATCH_SOURCES = { gmail: 'Gmail inbox' };
|
|
74
|
+
const REACH = {
|
|
75
|
+
message: 'Message me in Craft',
|
|
76
|
+
push: 'Notify my phone',
|
|
77
|
+
call: 'Call me when it is very important',
|
|
78
|
+
};
|
|
79
|
+
const HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
80
|
+
|
|
81
|
+
function normalizeAlwaysOn(a) {
|
|
82
|
+
a = a && typeof a === 'object' ? a : {};
|
|
83
|
+
const q = a.quiet && typeof a.quiet === 'object' ? a.quiet : {};
|
|
84
|
+
const watch = (Array.isArray(a.watch) ? a.watch : ['gmail']).filter((k) => WATCH_SOURCES[k]);
|
|
85
|
+
return {
|
|
86
|
+
on: !!a.on,
|
|
87
|
+
watch: watch.length ? [...new Set(watch)] : ['gmail'],
|
|
88
|
+
reach: REACH[a.reach] ? a.reach : 'push',
|
|
89
|
+
// Draft a reply in Gmail for important mail (one model call each). Off = just tell me.
|
|
90
|
+
draft: a.draft !== false,
|
|
91
|
+
quiet: { on: q.on !== false, from: HHMM.test(q.from) ? q.from : '22:00', to: HHMM.test(q.to) ? q.to : '07:00' },
|
|
92
|
+
// Keep watching from Codeply's server while this PC is off (stores the Gmail sign-in there, encrypted).
|
|
93
|
+
cloud: !!a.cloud,
|
|
94
|
+
// "Call me when X emails me" (watch_email): sender, call or text, once unless repeat.
|
|
95
|
+
alerts: (Array.isArray(a.alerts) ? a.alerts : []).filter((x) => x && typeof x.from === 'string' && x.from.trim())
|
|
96
|
+
.slice(-20).map((x) => ({ from: x.from.trim().toLowerCase().slice(0, 200), how: x.how === 'call' ? 'call' : 'text', note: String(x.note || '').slice(0, 200), repeat: !!x.repeat, at: Number(x.at) || Date.now() })),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
56
99
|
|
|
57
100
|
// Kept in sync with bot-avatar.js (the renderer). The engine only validates.
|
|
58
101
|
const AVATAR_KEYS = {
|
|
@@ -106,7 +149,7 @@ const TEMPLATES = [
|
|
|
106
149
|
specialty: 'Breaks a big goal into steps and gets the right teammate on each one',
|
|
107
150
|
instructions: 'Restate the objective in one line. Split it into a few small steps. Hand each step to the teammate whose domain fits, one at a time, with only the context they need. Read every result critically before moving on, redo or adjust when something is off, and finish with one merged answer that says what was done and what is left.',
|
|
108
151
|
tone: { preset: 'friendly', custom: '' },
|
|
109
|
-
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases'],
|
|
152
|
+
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases', 'calendar'],
|
|
110
153
|
avatar: { shape: 'burst9', eyes: 'pills', color: 'graphite' },
|
|
111
154
|
},
|
|
112
155
|
{
|
|
@@ -114,7 +157,7 @@ const TEMPLATES = [
|
|
|
114
157
|
specialty: 'Research: finds facts, docs and prior art, and cites where they came from',
|
|
115
158
|
instructions: 'Search the project and the web, read the primary source, and report what you found with links or file paths. Separate facts from guesses. Never change files.',
|
|
116
159
|
tone: { preset: 'professional', custom: '' }, sources: 'Official docs first, then the project itself, then reputable articles.',
|
|
117
|
-
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases'],
|
|
160
|
+
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases', 'calendar'],
|
|
118
161
|
avatar: { shape: 'drop', eyes: 'big', color: 'blue' },
|
|
119
162
|
},
|
|
120
163
|
{
|
|
@@ -122,7 +165,7 @@ const TEMPLATES = [
|
|
|
122
165
|
specialty: 'Outreach: drafts emails, posts and messages that sound like the user',
|
|
123
166
|
instructions: 'Write drafts the user can send as is: clear subject, short body, one ask. Match their voice from earlier messages. Never send anything yourself without the user saying yes.',
|
|
124
167
|
tone: { preset: 'friendly', custom: '' },
|
|
125
|
-
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases'],
|
|
168
|
+
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases', 'calendar'],
|
|
126
169
|
avatar: { shape: 'cloud', eyes: 'happy', color: 'pink', cheeks: true },
|
|
127
170
|
},
|
|
128
171
|
{
|
|
@@ -130,7 +173,7 @@ const TEMPLATES = [
|
|
|
130
173
|
specialty: 'Analysis: digs into code, data and numbers and explains what they mean',
|
|
131
174
|
instructions: 'Read the real code or data before concluding anything. Show the key numbers, the reasoning, and a clear recommendation. Say how sure you are.',
|
|
132
175
|
tone: { preset: 'direct', custom: '' },
|
|
133
|
-
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases'],
|
|
176
|
+
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases', 'calendar'],
|
|
134
177
|
avatar: { shape: 'squircle', eyes: 'ovals', color: 'teal', glasses: 'round' },
|
|
135
178
|
},
|
|
136
179
|
{
|
|
@@ -138,7 +181,7 @@ const TEMPLATES = [
|
|
|
138
181
|
specialty: 'Reporting: turns results into short, skimmable summaries and status updates',
|
|
139
182
|
instructions: 'Lead with the outcome, then 3 to 5 bullets, then next steps. Plain words, no jargon, nothing invented.',
|
|
140
183
|
tone: { preset: 'concise', custom: '' },
|
|
141
|
-
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases'],
|
|
184
|
+
approval: ['edit_files', 'run_commands', 'send', 'publish', 'databases', 'calendar'],
|
|
142
185
|
avatar: { shape: 'star', eyes: 'dots', color: 'orange', accessory: 'sprout' },
|
|
143
186
|
},
|
|
144
187
|
{
|
|
@@ -146,7 +189,7 @@ const TEMPLATES = [
|
|
|
146
189
|
specialty: 'Execution: writes and changes code, runs the checks, ships the change',
|
|
147
190
|
instructions: 'Make the smallest change that does the job, in the project\'s own style. Run the relevant check after every change and report what really happened.',
|
|
148
191
|
tone: { preset: 'concise', custom: '' },
|
|
149
|
-
approval: ['run_commands', 'send', 'publish', 'databases'],
|
|
192
|
+
approval: ['run_commands', 'send', 'publish', 'databases', 'calendar'],
|
|
150
193
|
avatar: { shape: 'burst7', eyes: 'sleepy', color: 'red', accessory: 'antenna' },
|
|
151
194
|
},
|
|
152
195
|
{
|
|
@@ -154,7 +197,7 @@ const TEMPLATES = [
|
|
|
154
197
|
specialty: 'Monitoring: checks that things still work and flags what changed or broke',
|
|
155
198
|
instructions: 'Run the checks, compare with what was expected, and report only what changed or failed, with the exact error. Suggest the next step, do not fix it yourself.',
|
|
156
199
|
tone: { preset: 'direct', custom: '' },
|
|
157
|
-
approval: ['edit_files', 'send', 'publish', 'databases'],
|
|
200
|
+
approval: ['edit_files', 'send', 'publish', 'databases', 'calendar'],
|
|
158
201
|
avatar: { shape: 'burst12', eyes: 'sparkle', color: 'purple', accessory: 'halo' },
|
|
159
202
|
},
|
|
160
203
|
];
|
|
@@ -190,8 +233,18 @@ function normalizeBot(input, existing) {
|
|
|
190
233
|
tone: { preset: TONES[tone.preset] ? tone.preset : 'friendly', custom: clip(tone.custom, 400) },
|
|
191
234
|
sources: clip(b.sources, 2000),
|
|
192
235
|
approval: Array.isArray(b.approval) ? [...new Set(b.approval.filter((k) => APPROVALS[k]))] : ALL_APPROVALS.slice(),
|
|
236
|
+
// Which categories existed when the user last chose; newer ones default to asking.
|
|
237
|
+
approvalSeen: ALL_APPROVALS.slice(),
|
|
193
238
|
avatar: normalizeAvatar(b.avatar),
|
|
239
|
+
// The voice it speaks with on calls (a Deepgram, Edge or Kokoro voice id; '' = pick one).
|
|
240
|
+
voice: typeof b.voice === 'string' && /^[\w.-]{0,80}$/.test(b.voice) ? b.voice : '',
|
|
241
|
+
alwaysOn: normalizeAlwaysOn(b.alwaysOn),
|
|
194
242
|
memory: cleanMemory(b.memory),
|
|
243
|
+
// Experience (learned on the job, see reflectOnRun): lessons, playbooks, tool tips, open threads.
|
|
244
|
+
lessons: cleanLessons(b.lessons),
|
|
245
|
+
playbooks: cleanPlaybooks(b.playbooks),
|
|
246
|
+
toolTips: cleanTips(b.toolTips),
|
|
247
|
+
openThreads: cleanThreads(b.openThreads),
|
|
195
248
|
template: typeof b.template === 'string' ? b.template : undefined,
|
|
196
249
|
createdAt: existing ? existing.createdAt : (Number(b.createdAt) || Date.now()),
|
|
197
250
|
updatedAt: Date.now(),
|
|
@@ -219,6 +272,10 @@ function listBots() {
|
|
|
219
272
|
for (const f of files) {
|
|
220
273
|
try {
|
|
221
274
|
const raw = JSON.parse(fs.readFileSync(path.join(botsDir(), f), 'utf8'));
|
|
275
|
+
if (Array.isArray(raw.approval)) {
|
|
276
|
+
const seen = Array.isArray(raw.approvalSeen) ? raw.approvalSeen : FIRST_APPROVALS;
|
|
277
|
+
for (const k of ALL_APPROVALS) if (!seen.includes(k) && !raw.approval.includes(k)) raw.approval.push(k);
|
|
278
|
+
}
|
|
222
279
|
const bot = normalizeBot(raw, { id: f.replace(/\.json$/, ''), createdAt: raw.createdAt });
|
|
223
280
|
bot.updatedAt = Number(raw.updatedAt) || bot.updatedAt;
|
|
224
281
|
out.push(bot);
|
|
@@ -353,6 +410,261 @@ Respond with ONLY a JSON object {"facts": ["..."]}. Use an empty list when nothi
|
|
|
353
410
|
}
|
|
354
411
|
}
|
|
355
412
|
|
|
413
|
+
// ─── Experience: learning on the job ────────────────────────────────────────
|
|
414
|
+
// Memory (above) is about the USER. Experience is about the WORK, after MUSE
|
|
415
|
+
// ("Learning on the Job", arxiv 2510.08002): after a run that used tools, the
|
|
416
|
+
// bot reflects once on what it did (reflectOnRun) and keeps
|
|
417
|
+
// - lessons: short <situation, what works> pairs, always in its prompt
|
|
418
|
+
// - playbooks: named procedures for work that succeeded (app / task, when
|
|
419
|
+
// to use, steps with the real tool names, precautions); a
|
|
420
|
+
// one-line index is always in the prompt, the ones that fit
|
|
421
|
+
// the current request go in full
|
|
422
|
+
// - toolTips: how tools behaved for this bot and user
|
|
423
|
+
// - openThreads: what it left unfinished, until a later run finishes it
|
|
424
|
+
// All bounded, deduplicated, and stored on the bot file like memory.
|
|
425
|
+
|
|
426
|
+
const SECRET_RE = /(api[_ -]?key|password|passwd|secret|token|bearer)\s*[:=]|\bsk-[a-z0-9]{8,}/i;
|
|
427
|
+
const noSecret = (s) => !SECRET_RE.test(String(s || ''));
|
|
428
|
+
const cleanList = (v, max, n) => (Array.isArray(v) ? v : [])
|
|
429
|
+
.filter((s) => typeof s === 'string' && s.trim() && noSecret(s))
|
|
430
|
+
.map((s) => clip(s, n)).slice(0, max);
|
|
431
|
+
|
|
432
|
+
function cleanLessons(list) {
|
|
433
|
+
return (Array.isArray(list) ? list : [])
|
|
434
|
+
.filter((l) => l && typeof l.situation === 'string' && typeof l.strategy === 'string' && l.situation.trim() && l.strategy.trim())
|
|
435
|
+
.map((l) => ({ situation: clip(l.situation, 160), strategy: clip(l.strategy, 240), at: Number(l.at) || Date.now() }))
|
|
436
|
+
.slice(-MAX_LESSONS);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function cleanPlaybook(p) {
|
|
440
|
+
if (!p || typeof p !== 'object') return null;
|
|
441
|
+
const app = clip(p.app, 40);
|
|
442
|
+
const task = clip(p.task, 80);
|
|
443
|
+
const steps = cleanList(p.steps, 10, 200);
|
|
444
|
+
if (!app || !task || !steps.length) return null;
|
|
445
|
+
return {
|
|
446
|
+
app, task, when: clip(p.when, 200), steps,
|
|
447
|
+
precautions: cleanList(p.precautions, 6, 200),
|
|
448
|
+
params: noSecret(p.params) ? clip(p.params, 300) : '',
|
|
449
|
+
uses: Math.max(1, Number(p.uses) || 1),
|
|
450
|
+
at: Number(p.at) || Date.now(),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function cleanPlaybooks(list) {
|
|
455
|
+
return (Array.isArray(list) ? list : []).map(cleanPlaybook).filter(Boolean).slice(-MAX_PLAYBOOKS);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function cleanTips(list) {
|
|
459
|
+
return (Array.isArray(list) ? list : [])
|
|
460
|
+
.map((t) => (typeof t === 'string' ? { tip: t } : t))
|
|
461
|
+
.filter((t) => t && typeof t.tip === 'string' && t.tip.trim())
|
|
462
|
+
.map((t) => ({ tip: clip(t.tip, 200), at: Number(t.at) || Date.now() }))
|
|
463
|
+
.slice(-MAX_TOOL_TIPS);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function cleanThreads(list) {
|
|
467
|
+
return (Array.isArray(list) ? list : [])
|
|
468
|
+
.map((t) => (typeof t === 'string' ? { text: t } : t))
|
|
469
|
+
.filter((t) => t && typeof t.text === 'string' && t.text.trim())
|
|
470
|
+
.map((t) => ({ text: clip(t.text, 200), at: Number(t.at) || Date.now() }))
|
|
471
|
+
.slice(-MAX_OPEN_THREADS);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const playbookKey = (p) => `${factKey(p.app)}|${factKey(p.task)}`;
|
|
475
|
+
const playbookName = (p) => `${p.app} / ${p.task}`;
|
|
476
|
+
const uniqueStrings = (list, max) => {
|
|
477
|
+
const out = [];
|
|
478
|
+
for (const s of list) if (!out.some((x) => sameFact(x, s))) out.push(s);
|
|
479
|
+
return out.slice(-max);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Pure merge of one reflection into a bot's experience. Same lesson
|
|
484
|
+
* situation, same tool tip, same app + task: the newer one replaces (a
|
|
485
|
+
* playbook is updated in place, keeping its use count and precautions).
|
|
486
|
+
* @returns {{experience:object, added:object}}
|
|
487
|
+
*/
|
|
488
|
+
function mergeExperience(exp, r, now = Date.now()) {
|
|
489
|
+
let lessons = cleanLessons(exp && exp.lessons);
|
|
490
|
+
let playbooks = cleanPlaybooks(exp && exp.playbooks);
|
|
491
|
+
let toolTips = cleanTips(exp && exp.toolTips);
|
|
492
|
+
let openThreads = cleanThreads(exp && exp.openThreads);
|
|
493
|
+
const added = { lessons: [], toolTips: [], playbook: null, updated: false, unfinished: '', resolved: [] };
|
|
494
|
+
for (const l of cleanLessons((r && r.lessons) || [])) {
|
|
495
|
+
if (!noSecret(l.situation + l.strategy)) continue;
|
|
496
|
+
lessons = lessons.filter((x) => !sameFact(x.situation, l.situation) && !sameFact(x.strategy, l.strategy));
|
|
497
|
+
lessons.push({ ...l, at: now });
|
|
498
|
+
added.lessons.push(l);
|
|
499
|
+
}
|
|
500
|
+
for (const t of cleanTips(((r && r.toolTips) || []).filter(noSecret))) {
|
|
501
|
+
toolTips = toolTips.filter((x) => !sameFact(x.tip, t.tip));
|
|
502
|
+
toolTips.push({ ...t, at: now });
|
|
503
|
+
added.toolTips.push(t.tip);
|
|
504
|
+
}
|
|
505
|
+
const pb = r && r.playbook ? cleanPlaybook(r.playbook) : null;
|
|
506
|
+
if (pb) {
|
|
507
|
+
const old = playbooks.find((x) => playbookKey(x) === playbookKey(pb));
|
|
508
|
+
const merged = old
|
|
509
|
+
? { ...pb, when: pb.when || old.when, params: pb.params || old.params, precautions: uniqueStrings([...old.precautions, ...pb.precautions], 6), uses: old.uses + 1, at: now }
|
|
510
|
+
: { ...pb, uses: 1, at: now };
|
|
511
|
+
playbooks = playbooks.filter((x) => x !== old);
|
|
512
|
+
playbooks.push(merged); // most recently used last, so the oldest unused one goes first
|
|
513
|
+
added.playbook = playbookName(merged);
|
|
514
|
+
added.updated = !!old;
|
|
515
|
+
}
|
|
516
|
+
// Open threads: resolved ones go (by number or by text), a new one is added.
|
|
517
|
+
const resolved = Array.isArray(r && r.resolved) ? r.resolved : [];
|
|
518
|
+
if (resolved.length) {
|
|
519
|
+
const before = openThreads;
|
|
520
|
+
openThreads = openThreads.filter((t, i) => !resolved.some((x) => (typeof x === 'number' ? x === i + 1 : (/^\d+$/.test(String(x).trim()) ? Number(x) === i + 1 : sameFact(t.text, x)))));
|
|
521
|
+
added.resolved = before.filter((t) => !openThreads.includes(t)).map((t) => t.text);
|
|
522
|
+
}
|
|
523
|
+
const unfinished = r && typeof r.unfinished === 'string' && noSecret(r.unfinished) ? clip(r.unfinished, 200) : '';
|
|
524
|
+
if (unfinished && !/^(none|nothing|n\/a|no)\.?$/i.test(unfinished)) {
|
|
525
|
+
openThreads = openThreads.filter((t) => !sameFact(t.text, unfinished));
|
|
526
|
+
openThreads.push({ text: unfinished, at: now });
|
|
527
|
+
added.unfinished = unfinished;
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
experience: {
|
|
531
|
+
lessons: lessons.slice(-MAX_LESSONS), playbooks: playbooks.slice(-MAX_PLAYBOOKS),
|
|
532
|
+
toolTips: toolTips.slice(-MAX_TOOL_TIPS), openThreads: openThreads.slice(-MAX_OPEN_THREADS),
|
|
533
|
+
},
|
|
534
|
+
added,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** Merge a reflection into the stored bot (re-read first, so a parallel learn is never lost). */
|
|
539
|
+
function addExperience(botId, reflection) {
|
|
540
|
+
const bot = getBot(botId);
|
|
541
|
+
if (!bot) return { bot: null, added: null };
|
|
542
|
+
const { experience, added } = mergeExperience(bot, reflection);
|
|
543
|
+
return { bot: save({ ...bot, ...experience, updatedAt: Date.now() }), added };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const EXPERIENCE_KINDS = ['lessons', 'playbooks', 'toolTips', 'openThreads'];
|
|
547
|
+
|
|
548
|
+
/** Forget one experience item (kind + index), or everything with kind 'all'. */
|
|
549
|
+
function forgetExperience(botId, kind, index) {
|
|
550
|
+
const bot = getBot(botId);
|
|
551
|
+
if (!bot) throw new Error('That bot is gone.');
|
|
552
|
+
if (kind === 'all') return save({ ...bot, lessons: [], playbooks: [], toolTips: [], openThreads: [], updatedAt: Date.now() });
|
|
553
|
+
if (!EXPERIENCE_KINDS.includes(kind)) throw new Error('Unknown kind of experience.');
|
|
554
|
+
const list = bot[kind].slice();
|
|
555
|
+
if (index >= 0 && index < list.length) list.splice(index, 1);
|
|
556
|
+
return save({ ...bot, [kind]: list, updatedAt: Date.now() });
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const STOP = new Set('the and for with that this from what when where which your you our are was were can could would should will have has had not but all any how who why into onto about please just some them they then than there these those its it him her his she use using want need make get got give show tell check look find out new one two'.split(' '));
|
|
560
|
+
|
|
561
|
+
/** Words that carry meaning, lightly stemmed ("emails" and "email" match). */
|
|
562
|
+
function terms(text) {
|
|
563
|
+
return [...new Set(String(text || '').toLowerCase().replace(/[^a-z0-9 ]+/g, ' ').split(/\s+/)
|
|
564
|
+
.filter((w) => w.length >= 3 && !STOP.has(w))
|
|
565
|
+
.map((w) => (w.length > 4 ? w.replace(/(ing|es|s)$/, '') : w)))];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** The playbooks that fit a request best (keyword overlap), at most `n`, best first. */
|
|
569
|
+
function rankPlaybooks(playbooks, request, n = PLAYBOOKS_IN_FULL) {
|
|
570
|
+
const want = terms(request);
|
|
571
|
+
if (!want.length) return [];
|
|
572
|
+
return (playbooks || []).map((p, i) => {
|
|
573
|
+
const head = terms(`${p.app} ${p.task}`);
|
|
574
|
+
const body = terms(`${p.when} ${(p.steps || []).join(' ')}`);
|
|
575
|
+
let score = 0;
|
|
576
|
+
for (const w of want) score += head.includes(w) ? 2 : body.includes(w) ? 0.5 : 0;
|
|
577
|
+
return { p, i, score };
|
|
578
|
+
}).filter((x) => x.score >= 2 || (x.score >= 1 && want.length <= 2))
|
|
579
|
+
.sort((a, b) => b.score - a.score || b.p.at - a.p.at)
|
|
580
|
+
.slice(0, n).map((x) => x.p);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** One line per tool call: name, short args, ok or the error. */
|
|
584
|
+
function trajectoryLines(steps) {
|
|
585
|
+
return (Array.isArray(steps) ? steps : []).filter((s) => s && s.name).slice(-30).map((s, i) => {
|
|
586
|
+
let args = '';
|
|
587
|
+
if (s.args && typeof s.args === 'object') {
|
|
588
|
+
const short = {};
|
|
589
|
+
for (const [k, v] of Object.entries(s.args)) {
|
|
590
|
+
if (/content|patch|body|text|old|new/i.test(k) && typeof v === 'string' && v.length > 60) short[k] = `(${v.length} chars)`;
|
|
591
|
+
else short[k] = typeof v === 'string' ? clip(v, 80) : v;
|
|
592
|
+
}
|
|
593
|
+
args = clip(JSON.stringify(short), 200);
|
|
594
|
+
} else if (s.label) args = clip(s.label, 140);
|
|
595
|
+
const result = s.ok ? 'ok' : `error${s.error ? `: ${clip(s.error, 140)}` : ''}`;
|
|
596
|
+
return `${i + 1}. ${s.name} ${args} -> ${result}`;
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function reflectPrompt(bot, { request, steps, reply }) {
|
|
601
|
+
const index = (bot.playbooks || []).map((p) => `- ${playbookName(p)}`).join('\n') || '(none yet)';
|
|
602
|
+
const lessons = (bot.lessons || []).map((l) => `- When ${l.situation}: ${l.strategy}`).join('\n') || '(none yet)';
|
|
603
|
+
const threads = (bot.openThreads || []).map((t, i) => `${i + 1}. ${t.text}`).join('\n') || '(none)';
|
|
604
|
+
return `You are the reflection step of an assistant called ${bot.name}${bot.specialty ? ` (${bot.specialty})` : ''}. It just finished a piece of work with its tools. Look at what it did and decide what it should learn for next time, so it gets better on the job.
|
|
605
|
+
|
|
606
|
+
The user's request:
|
|
607
|
+
${clip(request, 2000) || '(not given)'}
|
|
608
|
+
|
|
609
|
+
The tool calls, in order:
|
|
610
|
+
${trajectoryLines(steps).join('\n') || '(none)'}
|
|
611
|
+
|
|
612
|
+
${bot.name}'s final reply:
|
|
613
|
+
${clip(reply, 2000) || '(no reply)'}
|
|
614
|
+
|
|
615
|
+
Playbooks it already has (reuse the exact same app and task names to update one):
|
|
616
|
+
${index}
|
|
617
|
+
|
|
618
|
+
Lessons it already has:
|
|
619
|
+
${lessons}
|
|
620
|
+
|
|
621
|
+
Open threads it left unfinished before (numbered):
|
|
622
|
+
${threads}
|
|
623
|
+
|
|
624
|
+
Judge the outcome from the tool results, not from what the reply claims. Then respond with ONLY a JSON object:
|
|
625
|
+
{
|
|
626
|
+
"outcome": "success" or "partial" or "failed",
|
|
627
|
+
"playbook": null, or ONLY when it succeeded and the procedure is reusable: {"app": "the app or area, e.g. Gmail, Files, Web, Shell", "task": "short task name, e.g. find important emails", "when": "one line: when to use it", "steps": ["each key step, naming the real tool used, e.g. gmail_search with q=is:important newer_than:2d"], "precautions": ["what to watch out for"], "params": "parameters or values that worked, or empty"},
|
|
628
|
+
"lessons": [{"situation": "a short kind of situation", "strategy": "what works there, or what to avoid"}] (at most 2, only new and general ones),
|
|
629
|
+
"toolTips": ["tool_name: how it behaved, e.g. a query or argument that worked or failed"] (at most 2),
|
|
630
|
+
"unfinished": "one line about anything the user asked for that is still not done, or empty",
|
|
631
|
+
"resolved": [numbers of the open threads above that this work finished]
|
|
632
|
+
}
|
|
633
|
+
Never include passwords, keys, tokens or personal secrets. Keep every line short. Never use the long dash character.`;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* MUSE "Reflect + Memorize": after a run that used tools, one cheap model
|
|
638
|
+
* call turns the trajectory into experience on the bot. chatFn has the
|
|
639
|
+
* ai.js chatJson shape. Never throws.
|
|
640
|
+
* @param {object} bot
|
|
641
|
+
* @param {{request:string, steps:Array<{name:string,args?:object,label?:string,ok:boolean,error?:string}>, reply:string}} run
|
|
642
|
+
* @returns {Promise<{outcome?:string, added:object|null, skipped?:string}>}
|
|
643
|
+
*/
|
|
644
|
+
async function reflectOnRun(bot, run, chatFn) {
|
|
645
|
+
try {
|
|
646
|
+
if (!bot || !bot.id || typeof chatFn !== 'function') return { added: null, skipped: 'no bot or model' };
|
|
647
|
+
const steps = (run && Array.isArray(run.steps) ? run.steps : []).filter((s) => s && s.name);
|
|
648
|
+
if (!steps.length) return { added: null, skipped: 'no tools used' };
|
|
649
|
+
const fresh = getBot(bot.id) || normalizeBot(bot, bot);
|
|
650
|
+
const r = await chatFn([{ role: 'user', content: reflectPrompt(fresh, { ...run, steps }) }]);
|
|
651
|
+
if (!r || !r.success || !r.json || typeof r.json !== 'object') return { added: null, skipped: 'no answer' };
|
|
652
|
+
const j = r.json;
|
|
653
|
+
const outcome = ['success', 'partial', 'failed'].includes(j.outcome) ? j.outcome : 'partial';
|
|
654
|
+
const reflection = {
|
|
655
|
+
playbook: outcome === 'success' ? j.playbook : null,
|
|
656
|
+
lessons: (Array.isArray(j.lessons) ? j.lessons : []).slice(0, 2),
|
|
657
|
+
toolTips: (Array.isArray(j.toolTips) ? j.toolTips : []).slice(0, 2),
|
|
658
|
+
unfinished: j.unfinished,
|
|
659
|
+
resolved: j.resolved,
|
|
660
|
+
};
|
|
661
|
+
const { added } = addExperience(bot.id, reflection);
|
|
662
|
+
return { outcome, added };
|
|
663
|
+
} catch {
|
|
664
|
+
return { added: null, skipped: 'failed' };
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
356
668
|
// ─── Prompts ────────────────────────────────────────────────────────────────
|
|
357
669
|
|
|
358
670
|
function approvalLine(bot) {
|
|
@@ -370,10 +682,68 @@ const ASK_BOT_FORMAT = `To ask a teammate, write:
|
|
|
370
682
|
<codeply:ask_bot>
|
|
371
683
|
<bot>Name</bot>
|
|
372
684
|
<task>
|
|
373
|
-
The one task, with every bit of context they need
|
|
685
|
+
The one task, with every bit of context they need. Spell out the concrete details: email addresses, names, subjects, dates, file paths. Never write "the email from earlier" or "what they told you".
|
|
374
686
|
</task>
|
|
375
687
|
</codeply:ask_bot>`;
|
|
376
688
|
|
|
689
|
+
const PHONE_RULES = `YOUR LINE TO THE USER'S PHONE
|
|
690
|
+
You can reach the user on their phone with reach_me, and watch their inbox with watch_email (it keeps watching in the background).
|
|
691
|
+
- Urgent or time critical, or they asked for a call: reach_me with how=call (the phone rings and you speak).
|
|
692
|
+
- Anything else worth their attention: how=text (a notification).
|
|
693
|
+
- "Tell me / call me when X emails me": watch_email with from=X and how=call or text. Do not say you cannot run in the background.
|
|
694
|
+
- A reminder at a time: reach_me with at set to that time.
|
|
695
|
+
- "Have Shella call me": reach_me with bot set to that bot's name, so the call comes from them.`;
|
|
696
|
+
const PHONE_FORMAT = `To reach the phone or watch for an email, write:
|
|
697
|
+
<codeply:reach_me>
|
|
698
|
+
<how>call</how>
|
|
699
|
+
<message>Your Vercel deploy just failed on main.</message>
|
|
700
|
+
<at>17:30</at>
|
|
701
|
+
<bot>(optional: the bot it comes from)</bot>
|
|
702
|
+
</codeply:reach_me>
|
|
703
|
+
<codeply:watch_email>
|
|
704
|
+
<from>someone@example.com</from>
|
|
705
|
+
<how>call</how>
|
|
706
|
+
</codeply:watch_email>`;
|
|
707
|
+
|
|
708
|
+
const VERIFY_RULES = `BEFORE YOU SAY IT IS DONE
|
|
709
|
+
- Base every claim on what your tools actually returned in this run.
|
|
710
|
+
- Check the deliverable exists (the file is written, the message is sent, the command passed) before you say so.
|
|
711
|
+
- Never invent results, numbers, names or links. If something failed or you could not check it, say so plainly.`;
|
|
712
|
+
|
|
713
|
+
const shortDate = (at) => new Date(Number(at) || Date.now()).toISOString().slice(0, 10);
|
|
714
|
+
|
|
715
|
+
function playbookText(p) {
|
|
716
|
+
const lines = [`${playbookName(p)}`];
|
|
717
|
+
if (p.when) lines.push(`When to use: ${p.when}`);
|
|
718
|
+
lines.push('Steps:', ...(p.steps || []).map((s, i) => `${i + 1}. ${s}`));
|
|
719
|
+
if ((p.precautions || []).length) lines.push('Precautions:', ...p.precautions.map((s) => `- ${s}`));
|
|
720
|
+
if (p.params) lines.push(`What worked: ${p.params}`);
|
|
721
|
+
return lines.join('\n');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** The experience sections of a bot's prompt; `request` picks the playbooks shown in full. */
|
|
725
|
+
function experienceParts(bot, request) {
|
|
726
|
+
const parts = [];
|
|
727
|
+
const lessons = bot.lessons || [];
|
|
728
|
+
if (lessons.length) {
|
|
729
|
+
parts.push(`WHAT YOU HAVE LEARNED FROM PAST WORK\nStrategies from your own earlier work. Use them when the situation fits:\n${lessons.map((l) => `- When ${l.situation}: ${l.strategy}`).join('\n')}`);
|
|
730
|
+
}
|
|
731
|
+
const tips = bot.toolTips || [];
|
|
732
|
+
if (tips.length) parts.push(`TOOL TIPS\nHow your tools behaved before:\n${tips.map((t) => `- ${t.tip}`).join('\n')}`);
|
|
733
|
+
const playbooks = bot.playbooks || [];
|
|
734
|
+
if (playbooks.length) {
|
|
735
|
+
const top = rankPlaybooks(playbooks, request);
|
|
736
|
+
const index = playbooks.slice().reverse().map((p) => `- ${playbookName(p)}${p.when ? `: ${clip(p.when, 90)}` : ''}${top.includes(p) ? ' (in full below)' : ''}`);
|
|
737
|
+
parts.push(`YOUR PLAYBOOKS\nProcedures that worked for you before (app / task). Follow the matching one, and adapt it if things changed:\n${index.join('\n')}`);
|
|
738
|
+
if (top.length) parts.push(`PLAYBOOKS FOR THIS REQUEST\n${top.map(playbookText).join('\n\n')}`);
|
|
739
|
+
}
|
|
740
|
+
const threads = bot.openThreads || [];
|
|
741
|
+
if (threads.length) {
|
|
742
|
+
parts.push(`OPEN THREADS\nThings you left unfinished before. Bring one up when it fits, and finish it when the user wants:\n${threads.map((t) => `- (${shortDate(t.at)}) ${t.text}`).join('\n')}`);
|
|
743
|
+
}
|
|
744
|
+
return parts;
|
|
745
|
+
}
|
|
746
|
+
|
|
377
747
|
/**
|
|
378
748
|
* The system prompt addition for one bot.
|
|
379
749
|
* @param {object} bot
|
|
@@ -382,6 +752,7 @@ The one task, with every bit of context they need (they see nothing else).
|
|
|
382
752
|
* @param {boolean} [o.canDelegate] ask_bot is available in this run
|
|
383
753
|
* @param {string} [o.askedBy] set for a delegated sub-run: who asked
|
|
384
754
|
* @param {boolean} [o.native] native function calling (no tag example)
|
|
755
|
+
* @param {string} [o.request] the user's message: picks the playbooks shown in full
|
|
385
756
|
*/
|
|
386
757
|
function buildBotPrompt(bot, o = {}) {
|
|
387
758
|
if (!bot) return '';
|
|
@@ -396,8 +767,11 @@ function buildBotPrompt(bot, o = {}) {
|
|
|
396
767
|
parts.push(`APPROVAL BOUNDARY\n${approvalLine(bot)}`);
|
|
397
768
|
const memory = (bot.memory || []).slice(-MAX_MEMORY);
|
|
398
769
|
if (memory.length) {
|
|
399
|
-
parts.push(`WHAT YOU HAVE LEARNED ABOUT THIS USER\
|
|
770
|
+
parts.push(`WHAT YOU HAVE LEARNED ABOUT THIS USER\nBackground only. The current request and the recent chat always win: never assume an old item here is what the user means now (an older email, project or task). If the request is unclear, say which one you picked, or ask.\n${memory.map((m) => `- ${m.fact}`).join('\n')}`);
|
|
400
771
|
}
|
|
772
|
+
parts.push(...experienceParts(bot, o.request));
|
|
773
|
+
if (require('./reach.js').available()) parts.push(o.native ? PHONE_RULES : `${PHONE_RULES}\n\n${PHONE_FORMAT}`);
|
|
774
|
+
parts.push(VERIFY_RULES);
|
|
401
775
|
if (o.askedBy) {
|
|
402
776
|
parts.push(`DELEGATED TASK\n${o.askedBy} asked you to do one task. You only see that task, not the rest of the conversation. Do it fully with your tools, then reply in this shape:\nSummary: <one sentence with the outcome>\n<the details: findings, what you changed, anything ${o.askedBy} must know or check>`);
|
|
403
777
|
}
|
|
@@ -415,7 +789,10 @@ function buildBotPrompt(bot, o = {}) {
|
|
|
415
789
|
function teamPrompt(team, o = {}) {
|
|
416
790
|
const roster = rosterLines(team, null);
|
|
417
791
|
if (!roster.length) return '';
|
|
418
|
-
|
|
792
|
+
const phone = require('./reach.js').available()
|
|
793
|
+
? `\n\nWhen the user asks a bot to call or text them ("tell Shella to call me"), do it yourself with reach_me and bot set to that bot's name, so it comes from that bot. ${o.native ? '' : PHONE_FORMAT}`
|
|
794
|
+
: '';
|
|
795
|
+
return `BOTS\nThe user has these bots. When they ask for one by name, or a part of the work is clearly a bot's specialty, you can hand it that part with ask_bot. Only one bot runs at a time, and you wait for its answer.\n${roster.join('\n')}${o.native ? '' : `\n\n${ASK_BOT_FORMAT}`}${phone}`;
|
|
419
796
|
}
|
|
420
797
|
|
|
421
798
|
/** Which approval category a tool falls into, or null. */
|
|
@@ -522,7 +899,7 @@ async function delegate(o) {
|
|
|
522
899
|
try {
|
|
523
900
|
reply = await lock.run(o.token, () => o.runBot(target, {
|
|
524
901
|
task, depth: depth + 1, chain: [...chain, ...(o.caller ? [o.caller.id] : []), target.id],
|
|
525
|
-
prompt: buildBotPrompt(target, { team, askedBy: callerName, canDelegate: depth + 1 < MAX_DEPTH }),
|
|
902
|
+
prompt: buildBotPrompt(target, { team, askedBy: callerName, canDelegate: depth + 1 < MAX_DEPTH, request: task }),
|
|
526
903
|
}), o.signal);
|
|
527
904
|
} catch (e) {
|
|
528
905
|
const output = `${target.name} could not finish: ${e.message}`;
|
|
@@ -574,16 +951,116 @@ function fromDescription(json, description) {
|
|
|
574
951
|
for (const k of ['name', 'specialty', 'instructions', 'sources']) draft[k] = noDash(draft[k]);
|
|
575
952
|
draft.tone.custom = noDash(draft.tone.custom);
|
|
576
953
|
// Irreversible actions always stay behind approval, whatever the model said.
|
|
577
|
-
for (const k of ['send', 'publish', 'databases']) if (!draft.approval.includes(k)) draft.approval.push(k);
|
|
954
|
+
for (const k of ['send', 'publish', 'databases', 'calendar']) if (!draft.approval.includes(k)) draft.approval.push(k);
|
|
578
955
|
delete draft.id; delete draft.memory; delete draft.createdAt; delete draft.updatedAt;
|
|
956
|
+
delete draft.lessons; delete draft.playbooks; delete draft.toolTips; delete draft.openThreads;
|
|
579
957
|
return draft;
|
|
580
958
|
}
|
|
581
959
|
|
|
960
|
+
// ─── Voice calls ────────────────────────────────────────────────────────────
|
|
961
|
+
// A call turn is a real agent run: the bot keeps its tools (Gmail, files, the
|
|
962
|
+
// web, commands) and its approval boundary, and only the way it answers
|
|
963
|
+
// changes, so "check my email" on a call really checks it.
|
|
964
|
+
|
|
965
|
+
const VOICE_RULES = `LIVE VOICE CALL
|
|
966
|
+
You are on a live voice call with the user, talking out loud. Your final reply is spoken by a voice engine.
|
|
967
|
+
- You still have all your tools. When the user asks for real work (check their email, look something up, read or change a file), do it now with your tools, then tell them the result.
|
|
968
|
+
- Speak the result in one to three short sentences, the way people talk. Summarize: for emails say who it is from and what it is about, never read out long text, links or ids.
|
|
969
|
+
- No markdown, no lists, no headings, no emojis, no code, no links, no long dash in what you say.
|
|
970
|
+
- Ask one short question back when it helps.
|
|
971
|
+
- Anything that needs the user's OK (sending, changing files, running commands) is asked on their screen; if they say no, tell them plainly.`;
|
|
972
|
+
|
|
973
|
+
const VOICE_RULES_NO_TOOLS = `LIVE VOICE CALL
|
|
974
|
+
You are on a live voice call with the user, talking out loud. Everything you write is spoken by a voice engine.
|
|
975
|
+
- Reply in one to three short spoken sentences. Plain words, the way people talk.
|
|
976
|
+
- No markdown, no lists, no headings, no emojis, no code, no links, no long dash.
|
|
977
|
+
- Ask one short question back when it helps.
|
|
978
|
+
- Right now you cannot use your tools (email, files, the web), because the user's PC is offline. If they ask for that kind of work, say so in one sentence and offer to do it once their PC is on.`;
|
|
979
|
+
|
|
980
|
+
/** Whatever the model sent, make it sayable: no markdown, no long dashes. */
|
|
981
|
+
function spoken(s) {
|
|
982
|
+
const dash = String.fromCharCode(0x2014);
|
|
983
|
+
return String(s || '')
|
|
984
|
+
.replace(/<think>[\s\S]*?<\/think>/gi, '')
|
|
985
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
986
|
+
.replace(/\[(.*?)\]\((.*?)\)/g, '$1')
|
|
987
|
+
.replace(/https?:\/\/\S+/g, '')
|
|
988
|
+
.replace(/[*_#`>]+/g, '')
|
|
989
|
+
.split(dash).join(', ')
|
|
990
|
+
.replace(/\s+/g, ' ')
|
|
991
|
+
.trim();
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/** The call so far as chat turns: the last thing the user said is the new message. */
|
|
995
|
+
function callTurns(turns) {
|
|
996
|
+
const list = (Array.isArray(turns) ? turns : []).filter((t) => t && t.text).slice(-16)
|
|
997
|
+
.map((t) => ({ role: t.who === 'bot' ? 'assistant' : 'user', content: String(t.text).slice(0, 1200) }));
|
|
998
|
+
let lastUser = -1;
|
|
999
|
+
list.forEach((t, i) => { if (t.role === 'user') lastUser = i; });
|
|
1000
|
+
if (lastUser < 0) return { history: [], userMessage: '' };
|
|
1001
|
+
const history = [];
|
|
1002
|
+
for (const t of list.slice(0, lastUser)) {
|
|
1003
|
+
const prev = history[history.length - 1];
|
|
1004
|
+
if (prev && prev.role === t.role) prev.content += `\n${t.content}`; else history.push({ ...t });
|
|
1005
|
+
}
|
|
1006
|
+
while (history.length && history[0].role !== 'user') history.shift();
|
|
1007
|
+
return { history, userMessage: list.slice(lastUser).filter((t) => t.role === 'user').map((t) => t.content).join('\n') };
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* One spoken reply on a call, with tools. `runAgent` is the engine's agent
|
|
1012
|
+
* loop; `onStep({ name, label, done, ok })` reports tool use for the call
|
|
1013
|
+
* screen ("Checking Gmail..."). Returns { text } ready to speak, plus
|
|
1014
|
+
* { request, reply, steps } for reflectOnRun.
|
|
1015
|
+
*/
|
|
1016
|
+
async function voiceTurn({ bot, team, turns, recentChat, runAgent, route, cwd, signal, approve, onStep, maxSteps = 16 }) {
|
|
1017
|
+
const { history, userMessage } = callTurns(turns);
|
|
1018
|
+
if (!userMessage) return { text: '' };
|
|
1019
|
+
const extra = recentChat ? `\n\nRECENT CHAT BEFORE THIS CALL\n${recentChat}` : '';
|
|
1020
|
+
const run = runAgent({
|
|
1021
|
+
userMessage, history, mode: 'Build', cwd, signal, route, maxSteps, approve, botId: bot.id,
|
|
1022
|
+
botPrompt: () => `${buildBotPrompt(bot, { team, request: userMessage })}\n\n${VOICE_RULES}${extra}`,
|
|
1023
|
+
});
|
|
1024
|
+
let reply = '';
|
|
1025
|
+
const steps = []; // the trajectory, for reflectOnRun after the call turn
|
|
1026
|
+
for await (const ev of run) {
|
|
1027
|
+
if (ev.type === 'text' && !ev.interim) reply += (reply ? ' ' : '') + ev.text;
|
|
1028
|
+
else if (ev.type === 'tool_start' && onStep) onStep({ name: ev.name, args: ev.args || {} });
|
|
1029
|
+
else if (ev.type === 'tool_end') {
|
|
1030
|
+
steps.push(runStep(ev));
|
|
1031
|
+
if (onStep) onStep({ name: ev.name, args: ev.args || {}, done: true, ok: !!ev.ok });
|
|
1032
|
+
}
|
|
1033
|
+
else if (ev.type === 'error') throw new Error(ev.error || 'The run failed.');
|
|
1034
|
+
else if (ev.type === 'aborted') throw new Error('Stopped.');
|
|
1035
|
+
else if (ev.type === 'done') break;
|
|
1036
|
+
}
|
|
1037
|
+
if (signal && signal.aborted) throw new Error('Stopped.'); // hung up or talked over: nothing to say
|
|
1038
|
+
return { text: spoken(reply), request: userMessage, reply, steps };
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/** One agent tool_end event as a trajectory step for reflectOnRun. */
|
|
1042
|
+
function runStep(ev) {
|
|
1043
|
+
return { name: ev.name, args: ev.args || {}, ok: !!ev.ok, error: ev.ok ? undefined : clip(ev.summary || ev.error || '', 200) };
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/** What the call screen says while a tool runs. */
|
|
1047
|
+
const CALL_STEP = {
|
|
1048
|
+
gmail_search: 'Checking your email', gmail_send: 'Sending the email', gmail_draft: 'Saving a draft', drafts_list: 'Looking at your drafts',
|
|
1049
|
+
calendar_list: 'Checking your calendar', calendar_add: 'Adding it to your calendar', web_search: 'Searching the web', image_search: 'Looking for images', reach_me: 'Reaching your phone', watch_email: 'Setting an email alert', web_fetch: 'Reading a page',
|
|
1050
|
+
read_file: 'Reading a file', write_file: 'Writing a file', edit_file: 'Editing a file', apply_patch: 'Editing files', run: 'Running a command',
|
|
1051
|
+
search: 'Searching your files', list_dir: 'Looking through files', slack_post_message: 'Posting to Slack', browser_check: 'Checking the page',
|
|
1052
|
+
ask_bot: 'Asking a teammate', todo: 'Planning',
|
|
1053
|
+
};
|
|
1054
|
+
const callStepLabel = (name) => CALL_STEP[name] || 'Working on it';
|
|
1055
|
+
|
|
582
1056
|
module.exports = {
|
|
583
|
-
|
|
584
|
-
|
|
1057
|
+
VOICE_RULES, VOICE_RULES_NO_TOOLS, spoken, callTurns, voiceTurn, callStepLabel,
|
|
1058
|
+
MAX_MEMORY, MAX_DEPTH, ROLES, TONES, APPROVALS, AVATAR_KEYS, TEMPLATES, WATCH_SOURCES, REACH,
|
|
1059
|
+
botsDir, setBotsDir, normalizeBot, normalizeAvatar, normalizeAlwaysOn,
|
|
585
1060
|
listBots, getBot, findBot, createBot, createFromTemplate, updateBot, removeBot,
|
|
586
1061
|
mergeMemory, addMemory, forget, clearMemory, learnFromTurn,
|
|
1062
|
+
MAX_LESSONS, MAX_PLAYBOOKS, MAX_TOOL_TIPS, MAX_OPEN_THREADS,
|
|
1063
|
+
mergeExperience, addExperience, forgetExperience, rankPlaybooks, reflectOnRun, runStep,
|
|
587
1064
|
buildBotPrompt, teamPrompt, approvalCategory, canSkipApproval,
|
|
588
1065
|
createLock, globalLock, parseResult, delegate, botCard,
|
|
589
1066
|
describePrompt, fromDescription,
|