@fazer-ai/agents 1.0.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/agents/claude.js +152 -0
  4. package/dist/agents/codex.js +155 -0
  5. package/dist/agents/detect.js +15 -0
  6. package/dist/agents/handoff.js +22 -0
  7. package/dist/agents/hermes-skills.js +177 -0
  8. package/dist/agents/hermes.js +474 -0
  9. package/dist/agents/index.js +57 -0
  10. package/dist/agents/manual.js +22 -0
  11. package/dist/agents/other.js +39 -0
  12. package/dist/agents/shell.js +15 -0
  13. package/dist/agents/types.js +2 -0
  14. package/dist/config.js +48 -0
  15. package/dist/exec.js +279 -0
  16. package/dist/hostinger.js +75 -0
  17. package/dist/hub-command.js +144 -0
  18. package/dist/index.js +726 -0
  19. package/dist/licenses.js +93 -0
  20. package/dist/mcp.js +100 -0
  21. package/dist/oauth.js +578 -0
  22. package/dist/onboarding-marker.js +48 -0
  23. package/dist/preferences.js +40 -0
  24. package/dist/skills/agents-dev/SKILL.md +37 -0
  25. package/dist/skills/agents-dev/gotchas.md +6 -0
  26. package/dist/skills/agents-dev/guardrails.md +6 -0
  27. package/dist/skills/agents-dev/references/00-get-the-code.md +28 -0
  28. package/dist/skills/agents-dev/references/01-layout-and-bun-check.md +29 -0
  29. package/dist/skills/agents-dev/references/02-free-full-and-invariants.md +7 -0
  30. package/dist/skills/agents-dev/references/03-implement.md +9 -0
  31. package/dist/skills/agents-dev/references/04-own-image-and-deploy.md +13 -0
  32. package/dist/skills/agents-onboarding/SKILL.md +80 -0
  33. package/dist/skills/agents-onboarding/gotchas.md +157 -0
  34. package/dist/skills/agents-onboarding/guardrails.md +65 -0
  35. package/dist/skills/agents-onboarding/references/00-prereqs-and-access.md +37 -0
  36. package/dist/skills/agents-onboarding/references/01-vps-dns-ssh.md +67 -0
  37. package/dist/skills/agents-onboarding/references/01b-brownfield.md +70 -0
  38. package/dist/skills/agents-onboarding/references/01c-pick-tier.md +61 -0
  39. package/dist/skills/agents-onboarding/references/02-coolify.md +109 -0
  40. package/dist/skills/agents-onboarding/references/03-chatwoot-pro.md +61 -0
  41. package/dist/skills/agents-onboarding/references/04-agents-image.md +46 -0
  42. package/dist/skills/agents-onboarding/references/05-langfuse.md +45 -0
  43. package/dist/skills/agents-onboarding/references/06-setup-and-mcp.md +47 -0
  44. package/dist/skills/agents-onboarding/references/08-agent-import.md +55 -0
  45. package/dist/skills/agents-onboarding/references/09-chatwoot-bind.md +41 -0
  46. package/dist/skills/agents-onboarding/references/10-validate-e2e.md +34 -0
  47. package/dist/skills/agents-onboarding/references/agent-features.md +61 -0
  48. package/dist/skills/agents-onboarding/references/chatwoot-hub-register.md +69 -0
  49. package/dist/skills/agents-onboarding/references/deploy-b-portainer.md +138 -0
  50. package/dist/skills/agents-onboarding/references/deploy-c-compose.md +64 -0
  51. package/dist/skills/agents-onboarding/samples/agents/README.md +23 -0
  52. package/dist/skills/agents-onboarding/samples/agents/maria-clinica-moreira.json +313 -0
  53. package/dist/skills/agents-onboarding/scripts/chatwoot-admin.py +248 -0
  54. package/dist/skills/agents-onboarding/scripts/coolify.py +552 -0
  55. package/dist/skills/agents-onboarding/scripts/docker-status.py +129 -0
  56. package/dist/skills/agents-onboarding/scripts/gen-onboarding-env.ts +187 -0
  57. package/dist/skills/agents-onboarding/scripts/harbor-login.py +118 -0
  58. package/dist/skills/agents-onboarding/scripts/langfuse-verify.py +118 -0
  59. package/dist/skills/agents-onboarding/scripts/portainer-brownfield.py +115 -0
  60. package/dist/skills/agents-onboarding/scripts/remote.py +198 -0
  61. package/dist/skills/agents-onboarding/scripts/sshkey.py +140 -0
  62. package/dist/skills/agents-onboarding/templates/chatwoot/.env.example +30 -0
  63. package/dist/skills/agents-onboarding/templates/chatwoot/README.md +65 -0
  64. package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.coolify.yml +136 -0
  65. package/dist/skills/agents-onboarding/templates/chatwoot/docker-compose.yml +139 -0
  66. package/dist/skills/agents-onboarding/templates/docker-compose.coolify.yml +73 -0
  67. package/dist/skills/agents-onboarding/templates/docker-compose.portainer.yml +132 -0
  68. package/dist/skills/agents-onboarding/templates/docker-compose.prod.yml +85 -0
  69. package/dist/skills/agents-onboarding/templates/langfuse/.env.example +59 -0
  70. package/dist/skills/agents-onboarding/templates/langfuse/README.md +132 -0
  71. package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.coolify.yml +189 -0
  72. package/dist/skills/agents-onboarding/templates/langfuse/docker-compose.yml +185 -0
  73. package/dist/skills/agents-operation/SKILL.md +42 -0
  74. package/dist/skills/agents-operation/gotchas.md +61 -0
  75. package/dist/skills/agents-operation/guardrails.md +26 -0
  76. package/dist/skills/agents-operation/references/00-production-safety.md +24 -0
  77. package/dist/skills/agents-operation/references/01-diagnose.md +34 -0
  78. package/dist/skills/agents-operation/references/02-reproduce.md +22 -0
  79. package/dist/skills/agents-operation/references/03-adjust.md +36 -0
  80. package/dist/skills/agents-operation/references/04-validate-and-apply.md +31 -0
  81. package/dist/ui-select.js +279 -0
  82. package/dist/ui.js +167 -0
  83. package/package.json +53 -0
@@ -0,0 +1,189 @@
1
+ # Langfuse v3 (self-hosted) for fazer.ai: COOLIFY flavor.
2
+ #
3
+ # WHY THIS FILE EXISTS
4
+ # The Langfuse one-click/Coolify template ships the S3 env var NAMES but leaves the
5
+ # credentials + endpoint EMPTY and bundles NO object-storage service. Langfuse v3 REQUIRES an
6
+ # S3-compatible blob store for trace ingestion: every event JSON is uploaded to S3 BEFORE it is
7
+ # queued to ClickHouse. With empty creds the AWS SDK throws "Could not load credentials from any
8
+ # providers", the upload fails, and `POST /api/public/ingestion` returns HTTP 500, so NO trace is
9
+ # ever stored. The trap: `GET /api/public/projects` reads Postgres only, returns 200, and a naive
10
+ # "connection test" PASSES, hiding the broken ingestion. This compose bundles MinIO and wires all
11
+ # three S3 families (event / media / batch-export) to it.
12
+ #
13
+ # MAGIC VARS: Coolify auto-generates every `SERVICE_*` value on first deploy (incl.
14
+ # SERVICE_USER_MINIO / SERVICE_PASSWORD_MINIO) and injects it. Never hardcode a secret here.
15
+ #
16
+ # Generic / Portainer / EasyPanel / Dokploy (manual env): use ./docker-compose.yml + ./.env.example.
17
+
18
+ x-langfuse-env: &langfuse-env
19
+ # ── core ──
20
+ DATABASE_URL: 'postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgres:5432/${POSTGRES_DB:-langfuse-db}'
21
+ SALT: '${SERVICE_PASSWORD_SALT}'
22
+ ENCRYPTION_KEY: '${SERVICE_PASSWORD_64_LANGFUSE}'
23
+ NEXTAUTH_SECRET: '${SERVICE_BASE64_NEXTAUTHSECRET}'
24
+ NEXTAUTH_URL: '${SERVICE_URL_LANGFUSE}'
25
+ TELEMETRY_ENABLED: 'false'
26
+ LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: 'false'
27
+ # Agent flips this: 'false' for the initial deploy (so the user can self-register in the browser),
28
+ # then 'true' after the project + keys are seeded, to close signup. Default 'true' (secure) if unset.
29
+ AUTH_DISABLE_SIGNUP: '${AUTH_DISABLE_SIGNUP:-true}'
30
+ HOSTNAME: 0.0.0.0
31
+ # ── clickhouse ──
32
+ CLICKHOUSE_MIGRATION_URL: 'clickhouse://clickhouse:9000'
33
+ CLICKHOUSE_URL: 'http://clickhouse:8123'
34
+ CLICKHOUSE_USER: '${SERVICE_USER_CLICKHOUSE}'
35
+ CLICKHOUSE_PASSWORD: '${SERVICE_PASSWORD_CLICKHOUSE}'
36
+ CLICKHOUSE_CLUSTER_ENABLED: 'false'
37
+ # ── redis ──
38
+ REDIS_HOST: redis
39
+ REDIS_PORT: '6379'
40
+ REDIS_AUTH: '${SERVICE_PASSWORD_REDIS}'
41
+ # ── blob storage (MinIO): THE PIECE THE ONE-CLICK OMITTED ──
42
+ LANGFUSE_USE_AZURE_BLOB: 'false'
43
+ LANGFUSE_S3_EVENT_UPLOAD_BUCKET: langfuse
44
+ LANGFUSE_S3_EVENT_UPLOAD_REGION: auto
45
+ LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: 'http://minio:9000'
46
+ LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: '${SERVICE_USER_MINIO}'
47
+ LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: '${SERVICE_PASSWORD_MINIO}'
48
+ LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: 'true'
49
+ LANGFUSE_S3_EVENT_UPLOAD_PREFIX: events/
50
+ LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: langfuse
51
+ LANGFUSE_S3_MEDIA_UPLOAD_REGION: auto
52
+ LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: 'http://minio:9000'
53
+ LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: '${SERVICE_USER_MINIO}'
54
+ LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: '${SERVICE_PASSWORD_MINIO}'
55
+ LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: 'true'
56
+ LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: media/
57
+ LANGFUSE_S3_BATCH_EXPORT_ENABLED: 'false'
58
+ LANGFUSE_S3_BATCH_EXPORT_BUCKET: langfuse
59
+ LANGFUSE_S3_BATCH_EXPORT_REGION: auto
60
+ LANGFUSE_S3_BATCH_EXPORT_ENDPOINT: 'http://minio:9000'
61
+ LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID: '${SERVICE_USER_MINIO}'
62
+ LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY: '${SERVICE_PASSWORD_MINIO}'
63
+ LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE: 'true'
64
+ LANGFUSE_S3_BATCH_EXPORT_PREFIX: exports/
65
+ # ── ingestion tuning (matches the one-click defaults) ──
66
+ LANGFUSE_INGESTION_QUEUE_DELAY_MS: '1'
67
+ LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS: '1000'
68
+ # ── email (optional; blank = console only) ──
69
+ EMAIL_FROM_ADDRESS: admin@example.com
70
+ SMTP_CONNECTION_URL: ''
71
+
72
+ services:
73
+ langfuse:
74
+ image: 'langfuse/langfuse:3'
75
+ environment:
76
+ <<: *langfuse-env
77
+ # ── headless seed (agent-driven; empty = no seed) ──
78
+ # The agent provisions EVERYTHING on the first boot: a user (org owner) + org + project + key pair,
79
+ # with AUTH_DISABLE_SIGNUP=true from the start (default above), so signup is never open. The operator
80
+ # SIGNS IN (never signs up) with the seeded email + password and changes it; the agent already holds
81
+ # the keys (it generated them) and wires them into fazer.ai agents. One deploy, no redeploy, no open
82
+ # signup window; this is Langfuse's own recommended headless-init pattern. See references/05-langfuse.md.
83
+ # On Coolify the agent sets these via the service env editor / API before the (single) deploy.
84
+ LANGFUSE_INIT_ORG_ID: '${LANGFUSE_INIT_ORG_ID:-}'
85
+ LANGFUSE_INIT_ORG_NAME: '${LANGFUSE_INIT_ORG_NAME:-}'
86
+ LANGFUSE_INIT_PROJECT_ID: '${LANGFUSE_INIT_PROJECT_ID:-}'
87
+ LANGFUSE_INIT_PROJECT_NAME: '${LANGFUSE_INIT_PROJECT_NAME:-}'
88
+ LANGFUSE_INIT_PROJECT_PUBLIC_KEY: '${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-}'
89
+ LANGFUSE_INIT_PROJECT_SECRET_KEY: '${LANGFUSE_INIT_PROJECT_SECRET_KEY:-}'
90
+ LANGFUSE_INIT_USER_EMAIL: '${LANGFUSE_INIT_USER_EMAIL:-}'
91
+ LANGFUSE_INIT_USER_NAME: '${LANGFUSE_INIT_USER_NAME:-}'
92
+ LANGFUSE_INIT_USER_PASSWORD: '${LANGFUSE_INIT_USER_PASSWORD:-}'
93
+ # Coolify routes the public FQDN to this container's port 3000 via these magic vars.
94
+ SERVICE_FQDN_LANGFUSE_3000: '${SERVICE_FQDN_LANGFUSE_3000}'
95
+ SERVICE_URL_LANGFUSE_3000: '${SERVICE_URL_LANGFUSE_3000}'
96
+ depends_on:
97
+ postgres: { condition: service_healthy }
98
+ redis: { condition: service_healthy }
99
+ clickhouse: { condition: service_healthy }
100
+ minio: { condition: service_healthy }
101
+ healthcheck:
102
+ test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3000/api/public/health']
103
+ interval: 5s
104
+ timeout: 5s
105
+ retries: 10
106
+ restart: unless-stopped
107
+
108
+ langfuse-worker:
109
+ image: 'langfuse/langfuse-worker:3'
110
+ environment:
111
+ <<: *langfuse-env
112
+ depends_on:
113
+ postgres: { condition: service_healthy }
114
+ redis: { condition: service_healthy }
115
+ clickhouse: { condition: service_healthy }
116
+ minio: { condition: service_healthy }
117
+ restart: unless-stopped
118
+
119
+ minio:
120
+ image: 'minio/minio'
121
+ # Path-style bucket "langfuse" == the /data/langfuse directory; pre-creating it avoids a
122
+ # separate `mc mb` init container (this is the upstream Langfuse self-host recipe).
123
+ entrypoint: sh
124
+ command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
125
+ environment:
126
+ MINIO_ROOT_USER: '${SERVICE_USER_MINIO}'
127
+ MINIO_ROOT_PASSWORD: '${SERVICE_PASSWORD_MINIO}'
128
+ volumes:
129
+ - 'minio-data:/data'
130
+ healthcheck:
131
+ test: ['CMD', 'mc', 'ready', 'local']
132
+ interval: 5s
133
+ timeout: 5s
134
+ retries: 10
135
+ start_period: 5s
136
+ restart: unless-stopped
137
+
138
+ postgres:
139
+ image: 'postgres:17-alpine'
140
+ environment:
141
+ POSTGRES_DB: '${POSTGRES_DB:-langfuse-db}'
142
+ POSTGRES_USER: '${SERVICE_USER_POSTGRES}'
143
+ POSTGRES_PASSWORD: '${SERVICE_PASSWORD_POSTGRES}'
144
+ volumes:
145
+ - 'langfuse-postgres-data:/var/lib/postgresql/data'
146
+ healthcheck:
147
+ test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
148
+ interval: 5s
149
+ timeout: 5s
150
+ retries: 10
151
+ restart: unless-stopped
152
+
153
+ redis:
154
+ image: 'redis:8'
155
+ command:
156
+ - sh
157
+ - '-c'
158
+ - 'redis-server --requirepass "$SERVICE_PASSWORD_REDIS"'
159
+ volumes:
160
+ - 'langfuse-redis-data:/data'
161
+ healthcheck:
162
+ test: ['CMD-SHELL', 'redis-cli -a "$SERVICE_PASSWORD_REDIS" ping | grep -q PONG']
163
+ interval: 5s
164
+ timeout: 5s
165
+ retries: 10
166
+ restart: unless-stopped
167
+
168
+ clickhouse:
169
+ image: 'clickhouse/clickhouse-server:26.2.4.23'
170
+ environment:
171
+ CLICKHOUSE_DB: '${CLICKHOUSE_DB:-default}'
172
+ CLICKHOUSE_USER: '${SERVICE_USER_CLICKHOUSE}'
173
+ CLICKHOUSE_PASSWORD: '${SERVICE_PASSWORD_CLICKHOUSE}'
174
+ volumes:
175
+ - 'langfuse-clickhouse-data:/var/lib/clickhouse'
176
+ - 'langfuse-clickhouse-logs:/var/log/clickhouse-server'
177
+ healthcheck:
178
+ test: 'wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1'
179
+ interval: 5s
180
+ timeout: 5s
181
+ retries: 10
182
+ restart: unless-stopped
183
+
184
+ volumes:
185
+ minio-data:
186
+ langfuse-postgres-data:
187
+ langfuse-redis-data:
188
+ langfuse-clickhouse-data:
189
+ langfuse-clickhouse-logs:
@@ -0,0 +1,185 @@
1
+ # Langfuse v3 (self-hosted) for fazer.ai: GENERIC flavor (Portainer / EasyPanel / Dokploy / plain Docker).
2
+ #
3
+ # Same topology as ./docker-compose.coolify.yml, but every secret comes from a .env you fill in
4
+ # manually (Coolify generates those automatically; here you don't have that, so see ./.env.example
5
+ # and generate strong values yourself). Read ./README.md first, especially WHY MinIO is mandatory.
6
+ #
7
+ # cp .env.example .env # then fill every CHANGE_ME, then:
8
+ # docker compose up -d
9
+ #
10
+ # Put a reverse proxy (Traefik/Caddy/nginx) in front of the langfuse service (port 3000) for TLS.
11
+ # Portainer: paste this file as a Stack and provide the same variables via the Stack env editor
12
+ # instead of a .env file.
13
+
14
+ x-langfuse-env: &langfuse-env
15
+ # ── core ──
16
+ DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}'
17
+ SALT: '${LANGFUSE_SALT}'
18
+ ENCRYPTION_KEY: '${LANGFUSE_ENCRYPTION_KEY}'
19
+ NEXTAUTH_SECRET: '${LANGFUSE_NEXTAUTH_SECRET}'
20
+ NEXTAUTH_URL: '${LANGFUSE_PUBLIC_URL}'
21
+ TELEMETRY_ENABLED: 'false'
22
+ LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: 'false'
23
+ # Agent flips this: 'false' for the initial deploy (so the user can self-register in the browser),
24
+ # then 'true' after the project + keys are seeded, to close signup. Default 'true' (secure) if unset.
25
+ AUTH_DISABLE_SIGNUP: '${AUTH_DISABLE_SIGNUP:-true}'
26
+ HOSTNAME: 0.0.0.0
27
+ # ── clickhouse ──
28
+ CLICKHOUSE_MIGRATION_URL: 'clickhouse://clickhouse:9000'
29
+ CLICKHOUSE_URL: 'http://clickhouse:8123'
30
+ CLICKHOUSE_USER: '${CLICKHOUSE_USER}'
31
+ CLICKHOUSE_PASSWORD: '${CLICKHOUSE_PASSWORD}'
32
+ CLICKHOUSE_CLUSTER_ENABLED: 'false'
33
+ # ── redis ──
34
+ REDIS_HOST: redis
35
+ REDIS_PORT: '6379'
36
+ REDIS_AUTH: '${REDIS_PASSWORD}'
37
+ # ── blob storage (MinIO): MANDATORY for ingestion; see README ──
38
+ LANGFUSE_USE_AZURE_BLOB: 'false'
39
+ LANGFUSE_S3_EVENT_UPLOAD_BUCKET: langfuse
40
+ LANGFUSE_S3_EVENT_UPLOAD_REGION: auto
41
+ LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: 'http://minio:9000'
42
+ LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: '${MINIO_ROOT_USER}'
43
+ LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: '${MINIO_ROOT_PASSWORD}'
44
+ LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: 'true'
45
+ LANGFUSE_S3_EVENT_UPLOAD_PREFIX: events/
46
+ LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: langfuse
47
+ LANGFUSE_S3_MEDIA_UPLOAD_REGION: auto
48
+ LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: 'http://minio:9000'
49
+ LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: '${MINIO_ROOT_USER}'
50
+ LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: '${MINIO_ROOT_PASSWORD}'
51
+ LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: 'true'
52
+ LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: media/
53
+ LANGFUSE_S3_BATCH_EXPORT_ENABLED: 'false'
54
+ LANGFUSE_S3_BATCH_EXPORT_BUCKET: langfuse
55
+ LANGFUSE_S3_BATCH_EXPORT_REGION: auto
56
+ LANGFUSE_S3_BATCH_EXPORT_ENDPOINT: 'http://minio:9000'
57
+ LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID: '${MINIO_ROOT_USER}'
58
+ LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY: '${MINIO_ROOT_PASSWORD}'
59
+ LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE: 'true'
60
+ LANGFUSE_S3_BATCH_EXPORT_PREFIX: exports/
61
+ # ── ingestion tuning ──
62
+ LANGFUSE_INGESTION_QUEUE_DELAY_MS: '1'
63
+ LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS: '1000'
64
+ # ── email (optional) ──
65
+ EMAIL_FROM_ADDRESS: '${LANGFUSE_EMAIL_FROM:-admin@example.com}'
66
+ SMTP_CONNECTION_URL: '${LANGFUSE_SMTP_URL:-}'
67
+
68
+ services:
69
+ langfuse:
70
+ image: 'langfuse/langfuse:3'
71
+ environment:
72
+ <<: *langfuse-env
73
+ # ── headless seed (agent-driven; empty = no seed) ──
74
+ # The agent provisions EVERYTHING on the first boot: a user (org owner) + org + project + key pair,
75
+ # with AUTH_DISABLE_SIGNUP=true from the start (default above), so signup is never open. The operator
76
+ # SIGNS IN (never signs up) with the seeded email + password and changes it; the agent already holds
77
+ # the keys (it generated them) and wires them into fazer.ai agents. One deploy, no redeploy, no open
78
+ # signup window; this is Langfuse's own recommended headless-init pattern. See references/05-langfuse.md.
79
+ LANGFUSE_INIT_ORG_ID: '${LANGFUSE_INIT_ORG_ID:-}'
80
+ LANGFUSE_INIT_ORG_NAME: '${LANGFUSE_INIT_ORG_NAME:-}'
81
+ LANGFUSE_INIT_PROJECT_ID: '${LANGFUSE_INIT_PROJECT_ID:-}'
82
+ LANGFUSE_INIT_PROJECT_NAME: '${LANGFUSE_INIT_PROJECT_NAME:-}'
83
+ LANGFUSE_INIT_PROJECT_PUBLIC_KEY: '${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-}'
84
+ LANGFUSE_INIT_PROJECT_SECRET_KEY: '${LANGFUSE_INIT_PROJECT_SECRET_KEY:-}'
85
+ LANGFUSE_INIT_USER_EMAIL: '${LANGFUSE_INIT_USER_EMAIL:-}'
86
+ LANGFUSE_INIT_USER_NAME: '${LANGFUSE_INIT_USER_NAME:-}'
87
+ LANGFUSE_INIT_USER_PASSWORD: '${LANGFUSE_INIT_USER_PASSWORD:-}'
88
+ ports:
89
+ # Expose the web UI / ingestion API. Front it with your own TLS-terminating proxy.
90
+ - '${LANGFUSE_PORT:-3000}:3000'
91
+ depends_on:
92
+ postgres: { condition: service_healthy }
93
+ redis: { condition: service_healthy }
94
+ clickhouse: { condition: service_healthy }
95
+ minio: { condition: service_healthy }
96
+ healthcheck:
97
+ test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:3000/api/public/health']
98
+ interval: 5s
99
+ timeout: 5s
100
+ retries: 10
101
+ restart: unless-stopped
102
+
103
+ langfuse-worker:
104
+ image: 'langfuse/langfuse-worker:3'
105
+ environment:
106
+ <<: *langfuse-env
107
+ depends_on:
108
+ postgres: { condition: service_healthy }
109
+ redis: { condition: service_healthy }
110
+ clickhouse: { condition: service_healthy }
111
+ minio: { condition: service_healthy }
112
+ restart: unless-stopped
113
+
114
+ minio:
115
+ image: 'minio/minio'
116
+ entrypoint: sh
117
+ command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
118
+ environment:
119
+ MINIO_ROOT_USER: '${MINIO_ROOT_USER}'
120
+ MINIO_ROOT_PASSWORD: '${MINIO_ROOT_PASSWORD}'
121
+ volumes:
122
+ - 'minio-data:/data'
123
+ # Uncomment to reach the MinIO console at http://<host>:9001 (optional; keep internal in prod).
124
+ # ports:
125
+ # - '127.0.0.1:9001:9001'
126
+ healthcheck:
127
+ test: ['CMD', 'mc', 'ready', 'local']
128
+ interval: 5s
129
+ timeout: 5s
130
+ retries: 10
131
+ start_period: 5s
132
+ restart: unless-stopped
133
+
134
+ postgres:
135
+ image: 'postgres:17-alpine'
136
+ environment:
137
+ POSTGRES_DB: '${POSTGRES_DB}'
138
+ POSTGRES_USER: '${POSTGRES_USER}'
139
+ POSTGRES_PASSWORD: '${POSTGRES_PASSWORD}'
140
+ volumes:
141
+ - 'langfuse-postgres-data:/var/lib/postgresql/data'
142
+ healthcheck:
143
+ test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
144
+ interval: 5s
145
+ timeout: 5s
146
+ retries: 10
147
+ restart: unless-stopped
148
+
149
+ redis:
150
+ image: 'redis:8'
151
+ command:
152
+ - sh
153
+ - '-c'
154
+ - 'redis-server --requirepass "$REDIS_PASSWORD"'
155
+ volumes:
156
+ - 'langfuse-redis-data:/data'
157
+ healthcheck:
158
+ test: ['CMD-SHELL', 'redis-cli -a "$REDIS_PASSWORD" ping | grep -q PONG']
159
+ interval: 5s
160
+ timeout: 5s
161
+ retries: 10
162
+ restart: unless-stopped
163
+
164
+ clickhouse:
165
+ image: 'clickhouse/clickhouse-server:26.2.4.23'
166
+ environment:
167
+ CLICKHOUSE_DB: '${CLICKHOUSE_DB:-default}'
168
+ CLICKHOUSE_USER: '${CLICKHOUSE_USER}'
169
+ CLICKHOUSE_PASSWORD: '${CLICKHOUSE_PASSWORD}'
170
+ volumes:
171
+ - 'langfuse-clickhouse-data:/var/lib/clickhouse'
172
+ - 'langfuse-clickhouse-logs:/var/log/clickhouse-server'
173
+ healthcheck:
174
+ test: 'wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1'
175
+ interval: 5s
176
+ timeout: 5s
177
+ retries: 10
178
+ restart: unless-stopped
179
+
180
+ volumes:
181
+ minio-data:
182
+ langfuse-postgres-data:
183
+ langfuse-redis-data:
184
+ langfuse-clickhouse-data:
185
+ langfuse-clickhouse-logs:
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: agents-operation
3
+ description: "Modo operação do fazer.ai agents: debugar conversas em produção e corrigir comportamentos inesperados do agente. Investiga (conversa no Chatwoot + ExecutionLog/flowlog + traces no Langfuse), reproduz no playground, ajusta o agente (prompt/ferramentas/behavior/KB) e re-valida, com toda mutação aprovada. Use quando uma instância JÁ em produção se comporta de forma inesperada e precisa de diagnóstico/ajuste, não é onboarding (subir do zero) nem desenvolvimento de código."
4
+ ---
5
+
6
+ # Modo operação do fazer.ai agents
7
+
8
+ Pega uma instância **já em produção** que está se comportando de forma inesperada e leva de "a conversa do cliente deu errado" até "causa entendida, ajuste validado e aplicado com aprovação". Audiência: **operador de uma instância viva**. Para subir uma instância nova use `agents-onboarding`; para mexer no código-fonte use `agents-dev`.
9
+
10
+ ## ⚠️ Segurança de produção (lê primeiro)
11
+
12
+ Este modo **inverte** o fence do onboarding: lá o alvo é uma VPS de teste e "nada de produção"; aqui o alvo **é** produção.
13
+
14
+ - **Investigação read-only é livre** (ler conversas, logs, traces, config do agente, queries de leitura). Mutação **não**.
15
+ - **Toda mudança precisa de OK explícito do usuário para aquela mudança específica.** Autorização a um objetivo (corrigir um comportamento) não é autorização para escolher o método nem para aplicar sozinho. Proponha o diff/ajuste e espere o aval.
16
+ - **Nunca editar o DB de produção direto** para mudar estado da aplicação: use a UI/API/console da própria app (editor de agente, write tools de MCP **dry-run por padrão**). Write direto no DB fura o passo de publish/validação da app.
17
+ - **Sem segredo em log, output ou commit;** mascarar ao exibir.
18
+
19
+ ## O fluxo (references)
20
+
21
+ Siga em ordem; cada etapa é uma reference. Leia a da etapa antes de executá-la.
22
+
23
+ 1. [`references/00-production-safety.md`](references/00-production-safety.md): a postura invertida: read-only livre, toda mutação aprovada item a item, nunca DB direto, dry-run por padrão. **Lê primeiro.**
24
+ 2. [`references/01-diagnose.md`](references/01-diagnose.md): localizar a conversa (`display_id`), ler o `ExecutionLog` (`/logs`), traces no Langfuse, config do agente: isolar **qual estágio** (stt/embed/generate/tts/split/handoff) divergiu.
25
+ 3. [`references/02-reproduce.md`](references/02-reproduce.md): reconstituir o turno no **playground** (modelo real, isolado da conversa real).
26
+ 4. [`references/03-adjust.md`](references/03-adjust.md): corrigir na camada certa: prompt, grants (replace-the-set), behavior, grounding/KB. Console ou MCP (dry-run primeiro).
27
+ 5. [`references/04-validate-and-apply.md`](references/04-validate-and-apply.md): re-validar no playground, conversa de teste controlada (Inbox API) quando fizer sentido, aplicar só com aprovação (audit cobre o write).
28
+
29
+ Fronteiras duras em [`guardrails.md`](guardrails.md); armadilhas de diagnóstico em [`gotchas.md`](gotchas.md).
30
+
31
+ ## Guardrails
32
+
33
+ Resumo (detalhe em [`guardrails.md`](guardrails.md)):
34
+
35
+ - **Produção-first:** read-only livre; toda mutação aprovada item a item; nunca DB de produção direto.
36
+ - **Dry-run:** toda write tool de MCP previewa; aplica só com OK.
37
+ - **Estilo:** PT-BR com acentuação; sem em-dash; `fazer.ai` minúsculo.
38
+
39
+ ## Skills irmãs
40
+
41
+ - `agents-onboarding`: subir uma instância nova num VPS (do zero ao agente).
42
+ - `agents-dev`: trabalhar no código-fonte (Free/Full, implementar, gerar imagem).
@@ -0,0 +1,61 @@
1
+ # Gotchas: armadilhas de diagnóstico em produção
2
+
3
+ Cada uma é uma armadilha real (extraída dos docs de subsistema). Leia antes de concluir.
4
+
5
+ ## Leitura / DB
6
+
7
+ ### Query crua no DB sem tenant context retorna ZERO linhas (não "não existe")
8
+
9
+ O `DATABASE_URL` é a role runtime (não-superuser) e o RLS está ativo. Um `SELECT` em tabela tenant-scoped (ex.: `agents`) **sem** contexto de tenant retorna **zero linhas** em silêncio: a linha existe, o RLS está filtrando. **Não conclua que o registro sumiu.** Para diagnóstico cross-tenant, conecte como superuser via `MIGRATION_DATABASE_URL` (bypassa RLS, **read-only**, nunca mutar prod) ou `SET app.tenant_id = '<id>';` na sessão antes do `SELECT`.
10
+
11
+ ### Transcrição de áudio vive só no Chatwoot, não no nosso DB
12
+
13
+ A transcrição do STT é escrita de volta no **meta do attachment no Chatwoot** (`transcribed_text`), nunca espelhada no nosso DB (regra anti-PII: não espelhamos corpo de mensagem). Procurar o texto da transcrição no DB do fazer.ai agents não acha nada. Idem para o corpo das mensagens: a thread é lida ao vivo do Chatwoot, sem mirror.
14
+
15
+ ### O ExecutionLog não tem o texto da mensagem
16
+
17
+ `detail` é só ids/contagens/enums (PII-free, passa por redação); `errorMessage` é sanitizado. Você vê **qual** estágio falhou e **por quê** (enum/erro), não o conteúdo. Para o texto/raciocínio, vá ao **Langfuse** (session = threadId por-conversa).
18
+
19
+ ## Flowlog / estágios
20
+
21
+ ### `embed` não é emitido separado: a falha de embedding aparece em `generate`
22
+
23
+ `embed` está no vocabulário mas **não está wired** ainda. A busca RAG roda **dentro** do span `generate`, então um erro de embedding/grounding surge como erro de `generate`, não numa linha `embed`. Não espere uma linha `embed` no `/logs`.
24
+
25
+ ### STT é eager e tem `turnId` próprio
26
+
27
+ O STT roda **antes** do turno (na chegada da mensagem), então tem um `turnId` separado do turno que responde. No `/logs`, correlacione STT ao turno pelo **`threadId`/`conversationId`**, não pelo `turnId`.
28
+
29
+ ### `source` separa inbox de playground
30
+
31
+ `GET /v1/logs` filtra por `source` (default `inbox`). Erros do **playground** ficam em `source=playground` e **não** disparam alerta (só `inbox` paga alerta). Se você reproduziu no playground e não vê no feed padrão, troque o filtro `source`.
32
+
33
+ ## Memória do agente (checkpointer)
34
+
35
+ ### Duas chaves diferentes: memória (por contato+canal) vs correlação (por conversa)
36
+
37
+ A memória do grafo é por **contato+canal** (`tenant:instance:ci:<contactInboxId>`): abrange as conversas daquele contato **naquele canal**, então uma conversa nova reusa o histórico anterior, e canais diferentes do mesmo contato têm memórias **separadas**. Debounce, flowlog e a **session do Langfuse** usam a chave **por-conversa** (`tenant:instance:display_id`). "O agente lembrou de algo de outra conversa" pode ser memória de canal reusada (esperado), não bug.
38
+
39
+ ## Behavior (config do agente)
40
+
41
+ ### Sintomas que são config, não bug de código
42
+
43
+ - Respondeu **balão-a-balão** (uma resposta por mensagem) → **debounce off**.
44
+ - Não respondeu fora de horário / mandou template ou nota num envio proativo → **service-window**/business-hours (a janela de 24h só governa **proativo**; resposta reativa é sempre in-window).
45
+ - Não respondeu em áudio → **tts.mode** (`never` por padrão) ou credencial TTS faltando.
46
+ - Áudio recebido virou "peça texto" → **stt** desabilitado ou sem credencial.
47
+ - Não usou a base → grant **RAG** ausente/vazio (fail-closed) ou embedding do tenant não configurado.
48
+
49
+ ### Embedding é por-tenant (não por-KB nem do modelo do agente)
50
+
51
+ Sem o embedding configurado no nível do **tenant** (`tenant_settings`), os docs da KB vão para FAILED e o grounding falha. É config de tenant, separada da chave do modelo do agente. Reindexar a base não recupera docs já FAILED: use o **retry por documento** (MCP `knowledge_document_retry`).
52
+
53
+ ### Resposta em áudio só é PTT no WhatsApp se for Ogg/Opus
54
+
55
+ O TTS emite Ogg/Opus de propósito; mp3/wav chegariam como arquivo comum, não nota de voz. Se "o áudio chegou como arquivo", o formato é o suspeito.
56
+
57
+ ## Mutação
58
+
59
+ ### Write direto no DB pode quebrar no próximo restart
60
+
61
+ Mudar config da app direto no DB pula o publish/validação: o reader tipado não normaliza/clampa, o audit não registra, e o runtime pode reler do cache. Pode ficar OK agora e quebrar no restart. Sempre via UI/API/MCP da própria app.
@@ -0,0 +1,26 @@
1
+ # Guardrails: fronteiras que NÃO se cruzam
2
+
3
+ Valem em qualquer execução desta skill. Cruzar qualquer uma é **parar e perguntar**. Detalhe em [`references/00-production-safety.md`](references/00-production-safety.md).
4
+
5
+ ## Produção-first
6
+
7
+ - O alvo **é** produção. **Investigação read-only é livre** (ler conversas, logs, traces, config do agente, queries de leitura, playground). **Mutação não.**
8
+ - **Toda mudança precisa de OK explícito do usuário para aquela mudança específica.** Autorização a um objetivo (corrigir um comportamento) não é autorização para escolher o método nem para aplicar sozinho. Proponha o ajuste exato (campo, valor antigo → novo, diff) e espere o aval. Aprovação de uma mudança não se estende à próxima.
9
+ - **Nunca editar o DB de produção direto** para mudar estado da app. Use a UI/API/console da própria app (editor do agente, write tools de MCP). Write direto no DB fura o publish/validação e pode quebrar no próximo restart.
10
+ - Confirme que o **alvo é o recurso certo** (a conversa/agente/tenant certo) antes de qualquer mutação ou de postar numa conversa viva.
11
+
12
+ ## Dry-run
13
+
14
+ - Toda **write tool de MCP é dry-run por padrão**: previewa um diff e não aplica nada sem `dry_run:false` explícito. Rode o dry-run, mostre o diff, aplique só com OK. O apply grava `AuditLog`.
15
+ - As tools `conversation_*` (`reply`/`handoff`/`return`/`status`/`reengage`) têm **efeito externo real** numa conversa do cliente e não são reversíveis: o dry-run mostra o texto exato; aplicar só com OK.
16
+
17
+ ## Segredos
18
+
19
+ - **Nenhum segredo em log, output, commit ou arquivo plano.** Ao exibir, mascare.
20
+ - O fazer.ai agents referencia segredos por **nome de vault** (`vault:<id>`), nunca o valor; respeite: não tente extrair nem imprimir o plaintext. Credencial faltando → o usuário preenche no console fora de banda.
21
+
22
+ ## Estilo
23
+
24
+ - PT-BR com acentuação correta (escreva "não"/"ação", nunca "nao"/"acao").
25
+ - Nada de em-dash (—) nem en-dash (–): use vírgula, ponto, dois-pontos, parênteses ou reescreva.
26
+ - `fazer.ai` sempre minúsculo (slugs `fazer-ai` ok).
@@ -0,0 +1,24 @@
1
+ # 00: Segurança de produção (lê primeiro)
2
+
3
+ O alvo **é** produção. A postura é o inverso do onboarding (onde nada toca produção).
4
+
5
+ ## O que é livre
6
+
7
+ **Investigação read-only**, sem pedir: ler conversas no Chatwoot, ler o `ExecutionLog` (`GET /v1/logs`, página `/logs`), ler traces no Langfuse, ler a config do agente (editor ou `agent_get`/`agent_settings_get`/`agent_tools_get`), rodar o **playground** (modelo real, mas isolado da conversa), queries de leitura no DB.
8
+
9
+ ## O que exige OK explícito
10
+
11
+ **Toda mutação**, item a item. Inclui: editar prompt/modelo/grants/behavior/KB do agente, qualquer write tool de MCP com `dry_run:false`, postar/transferir/resolver numa conversa real (tools `conversation_*`: `reply`/`handoff`/`return`/`status`/`reengage`), mexer em credencial/vault, reindexar KB.
12
+
13
+ - Autorização a um **objetivo** ("conserta esse comportamento") não autoriza o **método** nem aplicar sozinho. Proponha o ajuste exato (qual campo, valor antigo → novo), mostre o diff, espere o aval.
14
+ - Aprovação de uma mudança não se estende à próxima.
15
+ - **Infra: reiniciar/redeployar/rebuildar serviços** (fazer.ai agents, Chatwoot, Langfuse, orquestrador), rodar **migrations**, ou mudar env/config viva. É mutação de produção, não diagnóstico: pode derrubar estado em voo (um restart do Coolify zera a fila de deploy) e um `migrate` pode ser destrutivo/irreversível. Proponha e espere o OK; nunca como atalho no meio de uma investigação.
16
+
17
+ ## Nunca
18
+
19
+ - **Editar o DB de produção direto para mudar estado da app.** Use a UI/API/console da própria app: editor do agente, write tools de MCP. Write direto no DB fura o passo de publish/validação (o reader tipado normaliza e faz clamp; o audit registra; o runtime relê) e pode deixar o sistema OK agora e quebrado no próximo restart.
20
+ - **Expor segredo** em log, output ou commit. Ao exibir, mascare. O fazer.ai agents retorna segredos só por referência (nome do vault), nunca o valor; respeite isso.
21
+
22
+ ## Dry-run é o padrão das write tools de MCP
23
+
24
+ Toda write tool previewa um diff campo-a-campo e **não aplica nada** sem `dry_run:false` explícito. Rode o dry-run primeiro, mostre o diff ao usuário, só então aplique com o OK dele. O apply grava um `AuditLog` (`actorType: "mcp"`).
@@ -0,0 +1,34 @@
1
+ # 01: Diagnosticar (isolar qual estágio divergiu)
2
+
3
+ Objetivo: de "a conversa deu errado" até "o estágio X divergiu por causa de Y". Tudo read-only.
4
+
5
+ ## 1. Localizar a conversa
6
+
7
+ - No Chatwoot, a conversa tem um **`display_id`** (o número visível). É a âncora.
8
+ - Chaves internas correlatas (você não digita, mas aparecem nos logs/traces):
9
+ - **Por-conversa** `tenant:instance:display_id`: usada por debounce, correlação de flowlog, fence de tenant e como **session no Langfuse**.
10
+ - **Memória do grafo** `tenant:instance:ci:<contactInboxId>`: o histórico que o agente "lembra" (por contato+canal). Uma conversa nova reusa essa memória; canais diferentes do mesmo contato têm memórias separadas.
11
+
12
+ ## 2. Ler o ExecutionLog do turno
13
+
14
+ Página **`/logs`** (cards agrupados por turno, paginação keyset) ou `GET /v1/logs` (TENANT_ADMIN) / MCP `logs_query`. Filtros: `conversationId`, `agentId`, `turnId`, `stage`, `level`, `since/until`, `source` (default `inbox`; `playground` é separado).
15
+
16
+ - **Um `turnId` por turno.** Cada linha é um estágio: `stt`, `embed`, `generate`, `tts`, `split`, `handoff` (+ erros). Filtre pelo `conversationId` e leia os turnos em ordem.
17
+ - O `detail` é livre de PII (ids/contagens/enums); `errorMessage` é sanitizado. Você vê **o que** falhou e **onde**, não o texto da mensagem.
18
+ - Leitura de sintomas:
19
+ - erro/anomalia em `stt` → transcrição do áudio (provider/credencial).
20
+ - resposta sem usar a KB, ou erro de embedding → ver `generate` (a busca RAG roda **dentro** do span `generate`; `embed` ainda não é emitido separado).
21
+ - resposta errada/ferramenta errada → `generate` (prompt, grants, chamadas de tool).
22
+ - sem áudio quando deveria → `tts`. Balões estranhos → `split`. Não transferiu → `handoff`.
23
+
24
+ ## 3. Trace no Langfuse
25
+
26
+ Abra o trace do turno (env `production` ou `production-playground`, **session = o threadId por-conversa** `tenant:instance:display_id`). Mostra a sequência de chamadas ao modelo + tools, inputs/outputs, latência. É onde você lê o raciocínio e as tool calls que o flowlog só resume.
27
+
28
+ ## 4. Inspecionar a config do agente
29
+
30
+ Editor do agente (abas General/Tools/Knowledge/Behavior) ou MCP read: `agent_get`, `agent_settings_get` (blocos `debounce`/`stt`/`tts`/`split`/`serviceWindow`/`grounding`, já normalizados), `agent_tools_get` (grants). Confirme se o comportamento observado bate com a config (ex.: respondeu balão-a-balão → debounce off; não respondeu fora de horário → service-window/business-hours).
31
+
32
+ ## 5. (Se preciso) estado do checkpointer
33
+
34
+ O grafo persiste o histórico por thread de memória. Se o agente "lembra" algo que não deveria (ou perdeu contexto), a causa pode estar no histórico acumulado naquela thread. Leitura para diagnóstico; **não** edite o checkpointer direto.
@@ -0,0 +1,22 @@
1
+ # 02: Reproduzir (no playground, sem tocar a conversa real)
2
+
3
+ Antes de mexer em qualquer config, reproduza o comportamento de forma isolada. O **playground** roda o **mesmo** modelo + system prompt + tools (knowledge/HTTP/MCP/integração) que produção, mas **sem** Chatwoot, webhook, debounce ou auto-reply: nada vaza para a conversa do cliente.
4
+
5
+ ## Como rodar
6
+
7
+ - **Console:** editor do agente → aba **Playground**. Chat panel (Enter envia, Reset reinicia). Cada resposta expande em `trace` (tool calls/resultados) + `sources` (grounding da KB).
8
+ - **MCP:** `agent_playground` (`mcp:read`; args `agent_id`, `message`, `thread_id?`) → `{ reply, threadId, trace, sources }`.
9
+ - **REST:** `POST /api/v1/agents/:id/playground { message, threadId? }` (TENANT_ADMIN).
10
+
11
+ O `enabled` do agente é ignorado no playground (você testa antes de ligar). Os toggles por-feature (`stt.enabled`/`vision.enabled`) são respeitados; resposta em áudio é um toggle manual (`forceAudio`).
12
+
13
+ ## Reconstituir o turno
14
+
15
+ Mande a mesma mensagem (ou a sequência) que disparou o problema. Para multimodal, o playground aceita `attachment` (base64/url). Use o `trace` + `sources` para ver se o agente chamou a tool certa, fez grounding na KB certa, e por que respondeu o que respondeu.
16
+
17
+ ## Limites a ter em mente
18
+
19
+ - **Não é simulação pura:** as tools de HTTP/integração do agente **executam de verdade** (uma write tool escreve). Se o agente tem tool que muda estado externo, reproduzir pode causar efeito colateral real. Avalie antes.
20
+ - Sem mirror/conversa, as vars de contato/prompt vêm vazias (`instanceId`/`conversationId` dummy). Comportamento que depende de dados da conversa real (nome do contato, atributos, histórico daquela conversa) não reproduz idêntico aqui: o playground isola o **agente**, não o **estado da conversa**.
21
+ - A thread do playground é **fenced** (`tenant:playground:agentId:uuid`): um `threadId` só é aceito se casar essa forma exata; qualquer outra (ex.: a thread de uma conversa real) é rejeitada. Não dá para "abrir" a conversa do cliente pelo playground.
22
+ - Memória multi-turno: o cliente segura o `threadId` retornado entre turnos; Reset começa nova sessão.
@@ -0,0 +1,36 @@
1
+ # 03: Ajustar (corrigir na camada certa)
2
+
3
+ Achada a causa, ajuste na camada que a explica. Via **console** (editor do agente) ou **MCP** (write tools, **dry-run primeiro**, aplica só com OK). Toda mutação segue o `00-production-safety.md`.
4
+
5
+ ## Em qual camada está o problema
6
+
7
+ | Sintoma | Camada | Onde | Tool MCP |
8
+ | --- | --- | --- | --- |
9
+ | Tom/conteúdo/decisão da resposta | **Prompt/instruções** | editor → General | `prompt_set` |
10
+ | Modelo errado/caro/lento, temperatura | **Modelo** | editor → General (seção Model, `modelConfig`) | `agent_update` |
11
+ | Usou/não usou a tool certa | **Grants de ferramentas** | editor → Tools | `agent_tools_set` |
12
+ | Resposta sem fundamento na base | **Grounding/KB** | editor → Knowledge | `agent_tools_set` (grant RAG), `knowledge_*` |
13
+ | Cadência/áudio/janela/agrupamento | **Behavior** | editor → Behavior | `agent_settings_set` |
14
+
15
+ ## Grants de ferramentas: replace-the-set
16
+
17
+ O editor (`Tools` + `Knowledge`) edita **um** working set de grants e faz **PUT do set inteiro** (substitui, não acumula). O `agent_tools_set` segue o mesmo modelo.
18
+
19
+ - **NATIVE:** sem grant NATIVE (ou allowlist vazia) = **todas** as tools nativas. Restringir = mandar o subconjunto explícito.
20
+ - **RAG:** habilitar = mandar os nomes da tool RAG + os ids das KBs. Vazio = sem RAG (fail-closed).
21
+ - MCP: discover por servidor → allowlist. INTEGRATION: checkboxes por toolpack.
22
+
23
+ ## Behavior: o que cada bloco controla (1 linha)
24
+
25
+ `agent.settings.*`, ajustável no editor → Behavior e via `agent_settings_set` (patch parcial, merge nas sub-chaves, re-lido pelos readers tipados com clamp):
26
+
27
+ - **debounce**: agrupa a rajada de mensagens e responde **uma vez** (on por padrão; `windowSeconds`, `maxMessagesPerBurst`, `maxWindowSeconds`).
28
+ - **stt**: transcreve áudios recebidos (on por padrão, efetivo só com credencial; `provider`/`model`/`language`/`credentialRef`).
29
+ - **tts**: responde em áudio: `mode` `never`|`mirror`|`preference` (default `never`).
30
+ - **split**: quebra a resposta em balões com "digitando" (off por padrão; só texto).
31
+ - **serviceWindow**: janela de 24h do WhatsApp para envios **proativos**: dentro = livre, fora = template HSM ou nota (on por padrão). Não afeta a resposta reativa.
32
+ - **grounding**: limiar de distância (`maxDistance`) da busca na KB (distinto do grant RAG da aba Knowledge).
33
+
34
+ ## Credenciais
35
+
36
+ Nunca passe o segredo cru. Na agents o segredo vive no **vault** e é referenciado por nome (`credentialRef` = `vault:<id>`); MCP traduz nome↔ref na borda, nunca o valor. Credencial faltando não é erro: a tool retorna `needsCredential` + URL do console para o usuário preencher fora de banda.