Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile.heroku
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Community-maintained template, not tested in CI.
# Thinker: https://github.com/patrickbrandao/thinker
FROM docker.io/tmsoftbrasil/thinker:0.33
FROM docker.io/tmsoftbrasil/thinker:0.34
2 changes: 1 addition & 1 deletion deploy/aws/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ The deploy script exposes the common settings as environment variables:
STACK_NAME=thinker \
SERVICE_NAME=thinker \
AWS_REGION=us-east-1 \
IMAGE_URL=docker.io/tmsoftbrasil/thinker:0.33 \
IMAGE_URL=docker.io/tmsoftbrasil/thinker:0.34 \
DATABASE_INSTANCE_CLASS=db.t4g.micro \
DESIRED_COUNT=1 \
./deploy/aws/deploy.sh
Expand Down
2 changes: 1 addition & 1 deletion deploy/aws/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

STACK_NAME="${STACK_NAME:-thinker}"
SERVICE_NAME="${SERVICE_NAME:-thinker}"
IMAGE_URL="${IMAGE_URL:-docker.io/tmsoftbrasil/thinker:0.33}"
IMAGE_URL="${IMAGE_URL:-docker.io/tmsoftbrasil/thinker:0.34}"
DESIRED_COUNT="${DESIRED_COUNT:-1}"
DATABASE_INSTANCE_CLASS="${DATABASE_INSTANCE_CLASS:-db.t4g.micro}"
DATABASE_DELETION_PROTECTION="${DATABASE_DELETION_PROTECTION:-false}"
Expand Down
2 changes: 1 addition & 1 deletion deploy/aws/thinker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Parameters:
ConstraintDescription: Use up to 40 lowercase letters, numbers, and hyphens, starting with a letter.
ImageUrl:
Type: String
Default: docker.io/tmsoftbrasil/thinker:0.33
Default: docker.io/tmsoftbrasil/thinker:0.34
Description: Thinker container image to deploy.
ContainerPort:
Type: Number
Expand Down
2 changes: 1 addition & 1 deletion deploy/coolify/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ curl -sSf https://<your-coolify-domain>/api/v1/health

## What Gets Provisioned

- Thinker Docker image `tmsoftbrasil/thinker:0.33`.
- Thinker Docker image `tmsoftbrasil/thinker:0.34`.
- PostgreSQL 18 container.
- Persistent Docker volume for PostgreSQL data.
- Persistent Docker volume for compressed request recordings at `/data/request-recordings`.
Expand Down
2 changes: 1 addition & 1 deletion deploy/coolify/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Thinker: https://github.com/patrickbrandao/thinker
services:
thinker:
image: tmsoftbrasil/thinker:0.33
image: tmsoftbrasil/thinker:0.34
restart: unless-stopped
stop_grace_period: 30s
environment:
Expand Down
6 changes: 3 additions & 3 deletions deploy/easypanel/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ If Thinker is available in your Easypanel template catalog:
1. Open your Easypanel project.
2. Choose **Templates**.
3. Search for **Thinker**.
4. Keep the default image `tmsoftbrasil/thinker:0.33` unless you need to pin another version.
4. Keep the default image `tmsoftbrasil/thinker:0.34` unless you need to pin another version.
5. Deploy the template.

The template provisions Thinker, PostgreSQL, a persistent request-recording volume, generated secrets, and a domain proxy on port `2099`.
Expand All @@ -43,7 +43,7 @@ Create the Thinker app service:
1. Click **Create Service**.
2. Choose **App**.
3. Set source type to **Docker image**.
4. Use image `tmsoftbrasil/thinker:0.33`.
4. Use image `tmsoftbrasil/thinker:0.34`.
5. Set the proxy port to `2099`.
6. Add your domain and mark it as the primary domain.

Expand Down Expand Up @@ -97,7 +97,7 @@ Before publishing a template, smoke test the full first-run flow:

## What Gets Provisioned

- Thinker Docker image `tmsoftbrasil/thinker:0.33`.
- Thinker Docker image `tmsoftbrasil/thinker:0.34`.
- PostgreSQL service.
- Generated or manually configured session and encryption secrets.
- HTTPS domain proxy to Thinker port `2099`.
Expand Down
2 changes: 1 addition & 1 deletion deploy/easypanel/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ templates/thinker/meta.yaml

The template provisions:

- Thinker app service from `tmsoftbrasil/thinker:0.33`.
- Thinker app service from `tmsoftbrasil/thinker:0.34`.
- PostgreSQL 18 service.
- Persistent app volume for compressed request recordings.
- Generated database password, Better Auth secret, and Thinker encryption key.
Expand Down
2 changes: 1 addition & 1 deletion deploy/easypanel/template/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ schema:
appServiceImage:
type: string
title: App Service Image
default: tmsoftbrasil/thinker:0.33
default: tmsoftbrasil/thinker:0.34
databaseServiceName:
type: string
title: Database Service Name
Expand Down
4 changes: 2 additions & 2 deletions deploy/fly/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Environment variables:
- `FLY_ORG`: Fly organization. Defaults to `personal`.
- `FLY_RECORDING_BUCKET_NAME`: private Tigris bucket name. Defaults to `<app-name>-recordings`. Keep the same value on reruns; changing an attached bucket requires a manual recording migration.

The script uses [`fly.toml`](fly.toml) as a template and deploys `docker.io/tmsoftbrasil/thinker:0.33`. It creates a private Tigris bucket and lets Fly stage the bucket's `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` secrets on the app; Thinker uses those credentials with the bucket name, Tigris endpoint, and `auto` region from `fly.toml`. Re-running the script keeps that bucket and the existing `BETTER_AUTH_SECRET` and `THINKER_ENCRYPTION_KEY` values; rotate those secrets manually only if you intend to invalidate sessions and encrypted provider credentials.
The script uses [`fly.toml`](fly.toml) as a template and deploys `docker.io/tmsoftbrasil/thinker:0.34`. It creates a private Tigris bucket and lets Fly stage the bucket's `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` secrets on the app; Thinker uses those credentials with the bucket name, Tigris endpoint, and `auto` region from `fly.toml`. Re-running the script keeps that bucket and the existing `BETTER_AUTH_SECRET` and `THINKER_ENCRYPTION_KEY` values; rotate those secrets manually only if you intend to invalidate sessions and encrypted provider credentials.

## Manual Deploy

Expand Down Expand Up @@ -112,7 +112,7 @@ fly logs --app <your-fly-app>

## What Gets Provisioned

- Fly app running `docker.io/tmsoftbrasil/thinker:0.33`.
- Fly app running `docker.io/tmsoftbrasil/thinker:0.34`.
- Fly Postgres app.
- Private Tigris S3-compatible bucket for compressed request recordings.
- `DATABASE_URL` secret from `fly postgres attach`.
Expand Down
2 changes: 1 addition & 1 deletion deploy/fly/fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ kill_signal = "SIGINT"
kill_timeout = "30s"

[build]
image = "docker.io/tmsoftbrasil/thinker:0.33"
image = "docker.io/tmsoftbrasil/thinker:0.34"

[env]
PORT = "2099"
Expand Down
2 changes: 1 addition & 1 deletion deploy/gcp/deploystack.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"name": "image_url",
"prompt": "Thinker image",
"type": "string",
"default": "docker.io/tmsoftbrasil/thinker:0.33"
"default": "docker.io/tmsoftbrasil/thinker:0.34"
},
{
"name": "database_tier",
Expand Down
2 changes: 1 addition & 1 deletion deploy/gcp/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ variable "service_name" {
variable "image_url" {
description = "Thinker container image to deploy."
type = string
default = "docker.io/tmsoftbrasil/thinker:0.33"
default = "docker.io/tmsoftbrasil/thinker:0.34"
}

variable "database_tier" {
Expand Down
4 changes: 2 additions & 2 deletions deploy/koyeb/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ openssl rand -hex 32
Open the Koyeb deploy link:

```text
https://app.koyeb.com/deploy?type=docker&image=docker.io%2Ftmsoftbrasil%2Fthinker%3A0.33&name=thinker&service_type=web&ports=2099%3Bhttp%3B%2F&env%5BPORT%5D=2099&env%5BDATABASE_URL%5D=postgres%3A%2F%2FUSER%3APASSWORD%40HOST%2FDB%3Fsslmode%3Drequire&env%5BBETTER_AUTH_SECRET%5D=replace-with-openssl-rand-hex-32&env%5BTHINKER_ENCRYPTION_KEY%5D=replace-with-different-openssl-rand-hex-32&env%5BBETTER_AUTH_URL%5D=https%3A%2F%2F%7B%7B+KOYEB_PUBLIC_DOMAIN+%7D%7D&env%5BBIND_ADDRESS%5D=0.0.0.0&env%5BDB_POOL_MAX%5D=8&env%5BAUTH_DB_POOL_MAX%5D=4&env%5BTHINKER_RECORDING_STORAGE%5D=s3&env%5BTHINKER_RECORDING_S3_BUCKET%5D=replace-with-private-bucket&env%5BTHINKER_RECORDING_S3_ENDPOINT%5D=https%3A%2F%2Fs3.example.com&env%5BTHINKER_RECORDING_S3_REGION%5D=us-east-1&env%5BTHINKER_RECORDING_S3_ACCESS_KEY_ID%5D=replace-with-limited-access-key&env%5BTHINKER_RECORDING_S3_SECRET_ACCESS_KEY%5D=replace-with-secret-key&env%5BTHINKER_RECORDING_S3_FORCE_PATH_STYLE%5D=false
https://app.koyeb.com/deploy?type=docker&image=docker.io%2Ftmsoftbrasil%2Fthinker%3A0.34&name=thinker&service_type=web&ports=2099%3Bhttp%3B%2F&env%5BPORT%5D=2099&env%5BDATABASE_URL%5D=postgres%3A%2F%2FUSER%3APASSWORD%40HOST%2FDB%3Fsslmode%3Drequire&env%5BBETTER_AUTH_SECRET%5D=replace-with-openssl-rand-hex-32&env%5BTHINKER_ENCRYPTION_KEY%5D=replace-with-different-openssl-rand-hex-32&env%5BBETTER_AUTH_URL%5D=https%3A%2F%2F%7B%7B+KOYEB_PUBLIC_DOMAIN+%7D%7D&env%5BBIND_ADDRESS%5D=0.0.0.0&env%5BDB_POOL_MAX%5D=8&env%5BAUTH_DB_POOL_MAX%5D=4&env%5BTHINKER_RECORDING_STORAGE%5D=s3&env%5BTHINKER_RECORDING_S3_BUCKET%5D=replace-with-private-bucket&env%5BTHINKER_RECORDING_S3_ENDPOINT%5D=https%3A%2F%2Fs3.example.com&env%5BTHINKER_RECORDING_S3_REGION%5D=us-east-1&env%5BTHINKER_RECORDING_S3_ACCESS_KEY_ID%5D=replace-with-limited-access-key&env%5BTHINKER_RECORDING_S3_SECRET_ACCESS_KEY%5D=replace-with-secret-key&env%5BTHINKER_RECORDING_S3_FORCE_PATH_STYLE%5D=false
```

In the deploy form:
Expand All @@ -45,7 +45,7 @@ In the deploy form:
- Leave `BETTER_AUTH_URL` as `https://{{ KOYEB_PUBLIC_DOMAIN }}`.
- Leave `PORT`, `BIND_ADDRESS`, `DB_POOL_MAX`, `AUTH_DB_POOL_MAX`, and `THINKER_RECORDING_STORAGE=s3` unchanged for a single-instance deploy. Set `THINKER_RECORDING_S3_FORCE_PATH_STYLE=true` only if your object-storage provider requires path-style URLs.

The button deploys `docker.io/tmsoftbrasil/thinker:0.33` and exposes port `2099` over HTTP.
The button deploys `docker.io/tmsoftbrasil/thinker:0.34` and exposes port `2099` over HTTP.

## Open Thinker

Expand Down
34 changes: 19 additions & 15 deletions design-system/registry/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,10 @@
{
"name": "Tabs",
"file": "src/components/ui.tsx"
},
{
"name": "WindowFooter",
"file": "src/components/ui.tsx"
}
],
"migrationMap": {},
Expand All @@ -2166,7 +2170,7 @@
"prop": "display",
"value": "inline-flex",
"file": "shell.css",
"line": 1080
"line": 1089
},
{
"selector": ".app.is-collapsed .user-trigger > svg",
Expand All @@ -2175,7 +2179,7 @@
"prop": "display",
"value": "inline-flex",
"file": "shell.css",
"line": 1080
"line": 1089
},
{
"selector": ".app.is-collapsed .user-trigger",
Expand All @@ -2184,21 +2188,21 @@
"prop": "padding",
"value": "6px 8px",
"file": "shell.css",
"line": 1089
"line": 1098
},
{
"selector": ".liquid-metal::after",
"prop": "inset",
"value": "-5px",
"file": "shell.css",
"line": 1255
"line": 1264
},
{
"selector": ".liquid-metal::after",
"prop": "padding",
"value": "5px",
"file": "shell.css",
"line": 1256
"line": 1265
}
],
"ghostRules": [
Expand All @@ -2209,8 +2213,8 @@
"prop": "display",
"value": "revert",
"file": "shell.css",
"line": 1073,
"losesTo": "shell.css:1080"
"line": 1082,
"losesTo": "shell.css:1089"
},
{
"selector": ".app.is-collapsed .user-trigger > svg",
Expand All @@ -2219,8 +2223,8 @@
"prop": "display",
"value": "revert",
"file": "shell.css",
"line": 1073,
"losesTo": "shell.css:1080"
"line": 1082,
"losesTo": "shell.css:1089"
},
{
"selector": ".app.is-collapsed .user-trigger",
Expand All @@ -2229,24 +2233,24 @@
"prop": "padding",
"value": "0 10px",
"file": "shell.css",
"line": 1085,
"losesTo": "shell.css:1089"
"line": 1094,
"losesTo": "shell.css:1098"
},
{
"selector": ".liquid-metal::after",
"prop": "inset",
"value": "-2px",
"file": "shell.css",
"line": 1211,
"losesTo": "shell.css:1255"
"line": 1220,
"losesTo": "shell.css:1264"
},
{
"selector": ".liquid-metal::after",
"prop": "padding",
"value": "2px",
"file": "shell.css",
"line": 1213,
"losesTo": "shell.css:1256"
"line": 1222,
"losesTo": "shell.css:1265"
}
],
"componentSheets": {
Expand Down
23 changes: 12 additions & 11 deletions design-system/registry/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ user-trigger (shell.css): user-trigger
well (base.css): well
wide (shell.css): wide

## Primitives (19)
## Primitives (20)
Avatar: src/components/ui.tsx
Badge: src/components/ui.tsx
Button: src/components/ui.tsx
Expand All @@ -319,23 +319,24 @@ Skel: src/components/ui.tsx
Status: src/components/ui.tsx
Switch: src/components/ui.tsx
Tabs: src/components/ui.tsx
WindowFooter: src/components/ui.tsx

## Migration map — legacy component → primitive to use instead (0)
(empty until primitives exist)

## Canonical rules — same selector+property declared twice with different values; the winner renders (5)
.app.is-collapsed .nav-action { display: inline-flex } ← shell.css:1080 [@media (max-width: 900px)]
.app.is-collapsed .user-trigger > svg { display: inline-flex } ← shell.css:1080 [@media (max-width: 900px)]
.app.is-collapsed .user-trigger { padding: 6px 8px } ← shell.css:1089 [@media (max-width: 900px)]
.liquid-metal::after { inset: -5px } ← shell.css:1255
.liquid-metal::after { padding: 5px } ← shell.css:1256
.app.is-collapsed .nav-action { display: inline-flex } ← shell.css:1089 [@media (max-width: 900px)]
.app.is-collapsed .user-trigger > svg { display: inline-flex } ← shell.css:1089 [@media (max-width: 900px)]
.app.is-collapsed .user-trigger { padding: 6px 8px } ← shell.css:1098 [@media (max-width: 900px)]
.liquid-metal::after { inset: -5px } ← shell.css:1264
.liquid-metal::after { padding: 5px } ← shell.css:1265

## Ghost rules — declared but always losing; edit the canonical rule instead, delete these in the refactor project (5)
.app.is-collapsed .nav-action { display: revert } at shell.css:1073 loses to shell.css:1080
.app.is-collapsed .user-trigger > svg { display: revert } at shell.css:1073 loses to shell.css:1080
.app.is-collapsed .user-trigger { padding: 0 10px } at shell.css:1085 loses to shell.css:1089
.liquid-metal::after { inset: -2px } at shell.css:1211 loses to shell.css:1255
.liquid-metal::after { padding: 2px } at shell.css:1213 loses to shell.css:1256
.app.is-collapsed .nav-action { display: revert } at shell.css:1082 loses to shell.css:1089
.app.is-collapsed .user-trigger > svg { display: revert } at shell.css:1082 loses to shell.css:1089
.app.is-collapsed .user-trigger { padding: 0 10px } at shell.css:1094 loses to shell.css:1098
.liquid-metal::after { inset: -2px } at shell.css:1220 loses to shell.css:1264
.liquid-metal::after { padding: 2px } at shell.css:1222 loses to shell.css:1265

## Component-loaded sheets — imported by components, not index.css; they load after the global styles (2)
canvas.css ← pages/Topology.tsx
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0006-organizacoes-e-papeis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ADR 0006 — Organizações, papéis e contas

- **Status:** aceito
- **Status:** aceito; item 6 alterado pelo [ADR 0010](0010-contas-so-pelo-super-usuario.md), que deixa só o super usuário (admin da instância) criar contas
- **Data:** 2026-10-27
- **Issues:** milestone v0.3 (#78, #102, #131); decisões em `docs/decisoes-iniciais.md` §2

Expand Down
21 changes: 21 additions & 0 deletions docs/adr/0010-contas-so-pelo-super-usuario.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ADR 0010 — Contas criadas só pelo super usuário

- **Status:** aceito
- **Data:** 2026-11-11
- **Altera:** [ADR 0006](0006-organizacoes-e-papeis.md), item 6, que deixava owner/admin de uma organização criar contas dentro dela

## Contexto

Pelo ADR 0006, adicionar um membro por e-mail na tela da organização criava a conta quando o e-mail não existia. Com isso, havia dois lugares para criar contas: **Usuários**, do admin da instância, e **Membros**, de cada organização. Na tela de membros também era possível trocar o papel de cada membro direto na lista.

## Decisão

1. Só o super usuário cria contas, na tela **Usuários**. "Super usuário" é o novo nome, na interface, do admin da instância. No código e na API ele continua sendo `isInstanceAdmin`.
2. `POST /organizations/:id/members` só adiciona uma conta que já existe. Para um e-mail sem conta, devolve 404 com `code: "account_not_found"` e não cria nada. A resposta mantém `temporaryPassword`, sempre `null`.
3. Na guia **Membros** só se adiciona uma conta existente ou se remove um membro. O papel é escolhido ao adicionar, e a lista não edita nada da conta.
4. Ao criar um super usuário com uma organização inicial, ele entra nela como `owner`. O papel é escolhido por quem cria só para contas comuns.

## Consequências

- Owner e admin de uma organização dependem do super usuário para criar uma conta nova.
- `PATCH /organizations/:id/members/:userId` (troca de papel) continua na API e no CLI, mas o painel não o usa mais.
2 changes: 1 addition & 1 deletion docs/dev/guia.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ Usuário (Better Auth) ──→ organization_members ──→ Tenant (organiza
- **Resolução** (`TenantCacheService.resolveAccess`, cache de 5 min invalidado em toda mudança de membro ou perfil): cabeçalho `X-Thinker-Organization`, senão `active_tenant_id`, senão a primeira associação. Organização pedida sem associação dá 403, exceto para o admin da instância, que entra como `owner`. Sem organização nenhuma, a primeira mutação cria uma pessoal (`ensureForUser`); remover um membro zera o `owner_user_id` que ele tinha na organização, e `ensureForUser` nunca reaproveita uma organização que tenha outros membros, então quem foi removido não volta como owner.
- Os guards anexam um `TenantContext` (`@TenantCtx()`) com `tenantId`, `userId`, `role`, `isInstanceAdmin` e `mustChangePassword`. Toda consulta filtra por tenant (`addTenantFilter`). O filtro por harness (`agent_name`) do log de requisições é resolvido uma vez por requisição para o id do agente vivo (`resolveAgentScope`, em `query-helpers.ts`) e reutilizado em todas as consultas; os caches de resultado usam o tenant mais o id resolvido (`agentScopeCacheKey`), nunca só o nome. **Nunca** escope, cacheie ou autorize por id de usuário. **Nunca** escope, cacheie ou autorize por id de usuário.
- Chaves de gestão (`api_keys`, chaves `thk_adm_` e tokens `thk_pat_` da CLI) representam quem as criou (ADR 0006 item 4): nas rotas escopadas valem na organização da chave com o papel atual dele ali; em `/api/v1/organizations/:id/*` e `/api/v1/admin/*` valem com o papel dele em cada organização e com o seu `isInstanceAdmin` (usado por `thinker org` e `thinker user`). Uma chave vazada equivale à conta inteira. O MCP limita as ferramentas de escrita pelo papel (`canWrite` em `mcp/mcp-auth.ts`: agentes pedem `member`; provedores e roteamento, `admin`).
- **Contas** (`organizations/`): o admin da instância e owner/admin de uma organização criam contas com senha temporária (`AccountsService`, via `runAsServerUserCreation`). Enquanto `must_change_password` estiver ligado, o `SessionGuard` só deixa passar `GET /api/v1/me` e `POST /api/v1/me/password` (403 `password_change_required`), e nenhuma chave de gestão da conta autentica (`ApiKeyAuthService` devolve o mesmo 403; no MCP a chave é recusada). A redefinição pelo admin apaga os tokens `thk_pat_` do `thinker login` da conta (as chaves `thk_adm_` continuam, bloqueadas até a troca) e as linhas de `session`, mas o cookie cache do Better Auth (5 min) mantém o cookie antigo válido nesse intervalo; como a flag volta a ligar e a troca exige a senha atual, esse cookie não faz nada além de ler `/me`.
- **Contas** (`organizations/`): só o admin da instância ("super usuário" no painel) cria contas, com senha temporária (`AccountsService`, via `runAsServerUserCreation`); `POST /organizations/:id/members` só adiciona contas existentes e devolve 404 `account_not_found` para e-mail sem conta ([ADR 0010](../adr/0010-contas-so-pelo-super-usuario.md)). Enquanto `must_change_password` estiver ligado, o `SessionGuard` só deixa passar `GET /api/v1/me` e `POST /api/v1/me/password` (403 `password_change_required`), e nenhuma chave de gestão da conta autentica (`ApiKeyAuthService` devolve o mesmo 403; no MCP a chave é recusada). A redefinição pelo admin apaga os tokens `thk_pat_` do `thinker login` da conta (as chaves `thk_adm_` continuam, bloqueadas até a troca) e as linhas de `session`, mas o cookie cache do Better Auth (5 min) mantém o cookie antigo válido nesse intervalo; como a flag volta a ligar e a troca exige a senha atual, esse cookie não faz nada além de ler `/me`.
- **Agent** (`agents`): pertence a um tenant; único por `[tenant_id, name]`.
- **AgentApiKey** (`agent_api_keys`): a chave `thk_*` do agente. Só autentica se a organização estiver ativa: apagar (desativar) a organização corta o proxy dos seus harnesses na hora, inclusive para chaves no cache do `AgentKeyAuthGuard`. **ApiKey** (`api_keys`): credencial de gestão do tenant, distinta da anterior.

Expand Down
9 changes: 9 additions & 0 deletions docs/releases/0.34.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Thinker 0.34.0

```bash
docker pull tmsoftbrasil/thinker:0.34.0
```

## Mudanças

- feat(dashboard): add windows with fixed title and footer, Users toolbar and editor window, Superuser rename, Organization tabs with members limited to existing accounts, new proxy in a floating window
Loading
Loading