@softize/opus 25.1.0 → 25.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/bin/cli.mjs +5 -0
- package/bin/lib/assistant-schema.d.mts +55 -0
- package/bin/lib/assistant-schema.mjs +286 -0
- package/bin/lib/check.mjs +173 -0
- package/bin/lib/copy.mjs +2 -0
- package/bin/lib/gen-manifest.mjs +3 -0
- package/bin/lib/gen-runner.mjs +29 -1
- package/bin/lib/mcp.mjs +1 -1
- package/docs/adr/0053-read-action-declares-the-resource-assistant.md +443 -0
- package/docs/protocol.md +54 -0
- package/package.json +5 -1
- package/registry/skills/create-opus-action/SKILL.md +8 -3
- package/registry/skills/create-opus-action/references/contract-and-binding.md +24 -1
- package/registry/skills/create-opus-action/references/evaluations.md +3 -0
- package/registry/skills/test-opus-action/SKILL.md +4 -1
- package/registry/skills/test-opus-action/references/evaluations.md +2 -0
- package/registry/skills/test-opus-action/references/harness.md +14 -0
- package/src/ai/index.ts +3 -0
- package/src/ai/resource-context.ts +113 -0
- package/src/core/assistant.ts +545 -0
- package/src/core/index.ts +23 -0
- package/src/core/runtime.ts +136 -0
- package/src/core/types.ts +34 -0
- package/src/mcp/index.ts +3 -0
- package/src/ui/docs/content/actions.md +97 -1
- package/src/ui/docs/content/ai.md +4 -2
- package/src/ui/docs/content/cli.md +2 -1
- package/src/ui/docs/content/mcp.md +3 -1
package/bin/lib/gen-runner.mjs
CHANGED
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
* automatable?: boolean, // aceita execução pelo sistema (ADR 0028)
|
|
54
54
|
* effect: 'read'|'write'|null, // efeito resolvido; list/view = read (ADR 0041)
|
|
55
55
|
* personalData: boolean|null, // devolve dado pessoal; null = não declarado
|
|
56
|
+
* ai: { enabled, description, destructive, requiresConfirmation }, // ADR 0053
|
|
57
|
+
* assistant?: { resource, skill, label, title, context, maxContextChars }, // view dona
|
|
56
58
|
* internal?: boolean,
|
|
57
59
|
* rateLimit?: { window, max },
|
|
58
60
|
* confirm?: ConfirmSpec,
|
|
@@ -67,6 +69,7 @@
|
|
|
67
69
|
|
|
68
70
|
import path from "node:path";
|
|
69
71
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
72
|
+
import { assistantSchemaIssues, serializeAiConfig } from "./assistant-schema.mjs";
|
|
70
73
|
|
|
71
74
|
const __filename = fileURLToPath(import.meta.url);
|
|
72
75
|
const __dirname = path.dirname(__filename);
|
|
@@ -90,7 +93,7 @@ async function main() {
|
|
|
90
93
|
const opusCoreUrl = pathToFileURL(
|
|
91
94
|
path.join(PACKAGE_ROOT, "src", "core", "index.ts"),
|
|
92
95
|
).href;
|
|
93
|
-
const { flattenDomain, domainPurges, declaredJsonSchema } = await import(opusCoreUrl);
|
|
96
|
+
const { flattenDomain, domainPurges, declaredJsonSchema, publicAssistant } = await import(opusCoreUrl);
|
|
94
97
|
|
|
95
98
|
const opusSeedUrl = pathToFileURL(
|
|
96
99
|
path.join(PACKAGE_ROOT, "src", "seed", "index.ts"),
|
|
@@ -138,6 +141,7 @@ async function main() {
|
|
|
138
141
|
const ctx = {
|
|
139
142
|
flattenDomain,
|
|
140
143
|
domainPurges,
|
|
144
|
+
publicAssistant,
|
|
141
145
|
// Standard Schema que declara o próprio JSON Schema (ADR 0051) dispensa o zod.
|
|
142
146
|
toJsonSchema: (schema) =>
|
|
143
147
|
declaredJsonSchema(schema, { target: "openapi-3.0" }) ??
|
|
@@ -393,6 +397,8 @@ function serializeAction(action, ctx) {
|
|
|
393
397
|
// ADR 0041: `list` e `view` são leitura pelo protocolo; null = não declarado.
|
|
394
398
|
effect: action.kind === "list" || action.kind === "view" ? "read" : (action.effect ?? null),
|
|
395
399
|
personalData: typeof action.personalData === "boolean" ? action.personalData : null,
|
|
400
|
+
// ADR 0053: a publicação para a IA entra no manifest, e a action dona, o assistente.
|
|
401
|
+
ai: serializeAiConfig(action.ai),
|
|
396
402
|
internal: action.internal === true,
|
|
397
403
|
successStatus: action.successStatus,
|
|
398
404
|
input: safeToJsonSchema(action.input, ctx),
|
|
@@ -434,6 +440,20 @@ function serializeAction(action, ctx) {
|
|
|
434
440
|
base.expand = action.expand ?? null;
|
|
435
441
|
}
|
|
436
442
|
|
|
443
|
+
if (action.kind === "view" && action.assistant !== undefined && action.assistant !== null) {
|
|
444
|
+
const issues = assistantSchemaIssues({
|
|
445
|
+
assistant: action.assistant,
|
|
446
|
+
input: base.input,
|
|
447
|
+
output: base.output,
|
|
448
|
+
});
|
|
449
|
+
if (issues.length > 0) {
|
|
450
|
+
throw declarationError(
|
|
451
|
+
`O assistente da action "${action.name}" não confere com o contrato (ADR 0053): ${issues.join("; ")}.`,
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
base.assistant = ctx.publicAssistant(action.assistant);
|
|
455
|
+
}
|
|
456
|
+
|
|
437
457
|
return base;
|
|
438
458
|
}
|
|
439
459
|
|
|
@@ -605,6 +625,14 @@ function emitError(message) {
|
|
|
605
625
|
process.exit(1);
|
|
606
626
|
}
|
|
607
627
|
|
|
628
|
+
/** Erro de declaração do consumidor: a mensagem basta, sem a pilha do runner. */
|
|
629
|
+
function declarationError(message) {
|
|
630
|
+
const err = new Error(message);
|
|
631
|
+
err.name = "OpusDeclarationError";
|
|
632
|
+
return err;
|
|
633
|
+
}
|
|
634
|
+
|
|
608
635
|
main().catch((err) => {
|
|
636
|
+
if (err?.name === "OpusDeclarationError") return emitError(err.message);
|
|
609
637
|
emitError(`runner crash: ${err.stack ?? err.message ?? String(err)}`);
|
|
610
638
|
});
|
package/bin/lib/mcp.mjs
CHANGED
|
@@ -42,7 +42,7 @@ export function buildServer() {
|
|
|
42
42
|
server.registerTool(
|
|
43
43
|
'opus_check',
|
|
44
44
|
{
|
|
45
|
-
description: 'Valida as convenções das actions (régua) — defineAction e o split defineContract+bindAction: naming, kind, ordem dos campos, export, requires-sem-authorize, ignored-by-kind, soft-delete-retention. Retorna findings.',
|
|
45
|
+
description: 'Valida as convenções das actions (régua) — defineAction e o split defineContract+bindAction: naming, kind, ordem dos campos, export, requires-sem-authorize, ignored-by-kind, assistant-declaration, soft-delete-retention. Retorna findings.',
|
|
46
46
|
inputSchema: { dir: z.string().optional() },
|
|
47
47
|
},
|
|
48
48
|
async ({ dir }) => asText(await scanDir(dirOf(dir))),
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
# ADR 0053 — A action de leitura declara o assistente do recurso
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-10-03.
|
|
5
|
+
- **Complementa:** ADR 0041 (efeito e dado pessoal no contrato) e ADR 0013 (Presentation como
|
|
6
|
+
artefato portátil orientado a action).
|
|
7
|
+
- **Relacionadas:** ADR 0111 da Grand Brasil, primeiro consumidor desta decisão; ADRs 0004, 0069,
|
|
8
|
+
0087, 0103 e 0104 da Grand Brasil.
|
|
9
|
+
|
|
10
|
+
## Contexto
|
|
11
|
+
|
|
12
|
+
Um produto feito com o Opus pode oferecer, além do chat geral, um chat especializado num recurso: a
|
|
13
|
+
conversa sobre um cliente, um relatório ou um fluxo. Esse chat precisa saber três coisas: como ler
|
|
14
|
+
o recurso e conferir se a pessoa ainda pode vê-lo, qual procedimento a IA segue ao tratar dele e
|
|
15
|
+
quais dados do recurso entram na conversa. Hoje nenhuma dessas três coisas está no contrato.
|
|
16
|
+
|
|
17
|
+
A Grand Brasil mostra o custo de deixá-las fora. Cada uma mora num lugar diferente, escrita à mão:
|
|
18
|
+
|
|
19
|
+
- O procedimento e a persona de cada tipo de recurso estão numa tabela no motor de IA
|
|
20
|
+
(`services/ai/src/resource-assistant.ts:17-61`).
|
|
21
|
+
- O dado que vai ao chat é montado na interface, tela por tela
|
|
22
|
+
(`apps/main/src/copilot/context.ts:89-139`).
|
|
23
|
+
- A conferência de acesso é uma rota com um `if` por tipo
|
|
24
|
+
(`services/main/src/server/main.ts:207-262`). Para relatórios, são duas rotas que chamam a regra
|
|
25
|
+
de acesso do domínio direto, sem passar pela action `report.read`
|
|
26
|
+
(`services/main/src/server/report-access-route.ts:22-37` e
|
|
27
|
+
`services/main/src/server/report-routes.ts:28-44`). A leitura do relatório por essas rotas não
|
|
28
|
+
gera registro de auditoria nem aparece na Lente.
|
|
29
|
+
|
|
30
|
+
O que o Opus já projeta não basta para mostrar esse desenho:
|
|
31
|
+
|
|
32
|
+
- O manifest projeta `effect` e `personalData`, mas não a configuração `ai`
|
|
33
|
+
(`bin/lib/gen-manifest.mjs:39-68`). Quem lê o manifest não sabe nem se a action está publicada
|
|
34
|
+
para a IA.
|
|
35
|
+
- A Lente (`@softize/lens`) lê o manifest e, por action, mostra nome, tipo, documentação,
|
|
36
|
+
permissão, eventos, entrada e saída (`lens/src/structure.ts:318-333`). Não mostra efeito, dado
|
|
37
|
+
pessoal, publicação para a IA nem assistente.
|
|
38
|
+
- `runtime.aiTools()` (`src/core/runtime.ts:997-1037`) e o `ListTools` do MCP
|
|
39
|
+
(`src/mcp/index.ts:54-60`) carregam efeito, dado pessoal e Produtos de Dados. O motor de IA da
|
|
40
|
+
Grand Brasil já lê esses metadados pelo MCP (`services/ai/src/mcp.ts:93-101`). É um canal pronto
|
|
41
|
+
para levar a declaração ao host.
|
|
42
|
+
- A Presentation já declara `assistant: { triggerLabel }` (`src/core/presentation.ts:125-130` e
|
|
43
|
+
`160`). Essa declaração cobre só a interface: o botão e a divisão da superfície. Não diz o que o
|
|
44
|
+
chat faz.
|
|
45
|
+
|
|
46
|
+
As forças em tensão:
|
|
47
|
+
|
|
48
|
+
- **Um lugar só.** Leitura, conferência de acesso, procedimento e dado ao modelo precisam vir da
|
|
49
|
+
mesma declaração, para não divergirem.
|
|
50
|
+
- **Contrato compartilhável.** O contrato roda no cliente e no servidor. A declaração não pode
|
|
51
|
+
carregar função de servidor, banco nem segredo, e precisa caber no manifest.
|
|
52
|
+
- **O procedimento é dado do consumidor.** Na Grand Brasil, Habilidades são registros
|
|
53
|
+
administráveis no banco do serviço de IA. O Opus não as conhece, e o `opus check`, que é
|
|
54
|
+
estático, não as alcança.
|
|
55
|
+
- **O dado ao modelo é mínimo, limitado e tratado como dado.** A Grand Brasil já decidiu que texto
|
|
56
|
+
vindo de fora entra no turno como bloco delimitado, nunca como instrução (ADR 0004 dela, seção
|
|
57
|
+
3), e pôs teto no que entra no turno (seção 2 da mesma ADR). Uma projeção sem teto repetiria o
|
|
58
|
+
problema de custo e de diluição do prompt que motivou aquela decisão.
|
|
59
|
+
- **Dado pessoal.** O dado do recurso pode identificar pessoas. A marca de exposição já é do
|
|
60
|
+
runtime (ADR 0041) e precisa continuar valendo.
|
|
61
|
+
- **Fronteira do SDK.** Persona, regras de negócio e o que cada tipo de recurso significa são do
|
|
62
|
+
consumidor.
|
|
63
|
+
|
|
64
|
+
## Decisão
|
|
65
|
+
|
|
66
|
+
### 1. Uma action `view` pode declarar o assistente do recurso que ela lê
|
|
67
|
+
|
|
68
|
+
O contrato ganha o campo opcional `assistant`. A action que o declara passa a ser a **action dona**
|
|
69
|
+
daquele tipo de recurso: é por ela que o recurso é lido e que o acesso a ele é conferido.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
export const customerViewContract = defineContract({
|
|
73
|
+
name: 'customer.view',
|
|
74
|
+
kind: 'view',
|
|
75
|
+
label: 'Ver cliente',
|
|
76
|
+
description: 'Reúne o cadastro canônico, a qualidade dos contatos e o relacionamento comercial.',
|
|
77
|
+
input: z.object({ id: z.string().uuid() }),
|
|
78
|
+
output: customerViewSchema,
|
|
79
|
+
personalData: true,
|
|
80
|
+
requires: commercialRead,
|
|
81
|
+
authorize: anyPerm(...commercialRead),
|
|
82
|
+
ai: true,
|
|
83
|
+
assistant: {
|
|
84
|
+
resource: 'customer',
|
|
85
|
+
skill: 'review-customer',
|
|
86
|
+
label: 'Assistente do cliente',
|
|
87
|
+
title: 'name',
|
|
88
|
+
context: ['kind', 'stage', 'sources', 'leads.totalCount', 'leads.latest.status'],
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
O exemplo deixa fora do contexto contato, documento, data de nascimento e endereço. O nome já vai
|
|
94
|
+
como `title` e não se repete em `context`.
|
|
95
|
+
|
|
96
|
+
| Campo | O que declara |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `resource` | O tipo de recurso de que a action é dona. Identificador em camelCase, único no runtime. |
|
|
99
|
+
| `skill` | A chave do procedimento que a IA segue ao tratar desse recurso, no registro do consumidor. |
|
|
100
|
+
| `label` | O nome do assistente para pessoas. Entra no inventário do `opus copy`. |
|
|
101
|
+
| `title` | O caminho, na saída, do campo que nomeia o recurso para pessoas. |
|
|
102
|
+
| `context` | Os caminhos, na saída, dos campos que o host pode entregar ao modelo como dado do recurso. |
|
|
103
|
+
| `maxContextChars` | Opcional. Teto do dado serializado: padrão de 4.000 caracteres, mínimo de 1.000 e máximo de 16.000. |
|
|
104
|
+
|
|
105
|
+
Regras da declaração:
|
|
106
|
+
|
|
107
|
+
- Só uma action `view` declara assistente. A leitura de um recurso identificado é o papel da `view`.
|
|
108
|
+
- A action precisa estar publicada para a IA (`ai` habilitado). Um assistente cujo recurso a IA
|
|
109
|
+
não pode ler seria uma contradição.
|
|
110
|
+
- A action precisa declarar `personalData`, `true` ou `false`. Com assistente, a declaração deixa
|
|
111
|
+
de ser opcional, porque decide como o host trata o dado que entrega ao modelo.
|
|
112
|
+
- A entrada é um objeto em que `id`, do tipo texto, é o único campo obrigatório; outros campos, se
|
|
113
|
+
houver, têm valor padrão. É só com `{ id }` que o runtime pede o recurso.
|
|
114
|
+
- Os caminhos usam ponto para descer em objetos (`leads.totalCount`). Um caminho que passa por uma
|
|
115
|
+
lista se aplica a cada item. Um caminho pode atravessar um campo anulável, como `leads` em
|
|
116
|
+
`customer.view`: quando o valor intermediário é `null`, a projeção entrega `null` naquele ponto.
|
|
117
|
+
- Cada `resource` tem uma única action dona no runtime.
|
|
118
|
+
|
|
119
|
+
`assistant` fica no grupo de integração da ordem canônica, o mesmo de `ai`. O `opus check` confere
|
|
120
|
+
o grupo, não a posição dentro dele (`bin/lib/check.mjs:48-77`).
|
|
121
|
+
|
|
122
|
+
### 2. O runtime lê o recurso pela action dona
|
|
123
|
+
|
|
124
|
+
O runtime ganha duas leituras:
|
|
125
|
+
|
|
126
|
+
- **`runtime.assistants()`** devolve a lista das declarações registradas, com a action dona de
|
|
127
|
+
cada uma. Serve ao consumidor para montar rotas e telas a partir da declaração, sem lista
|
|
128
|
+
paralela, e para conferir as chaves de procedimento onde também tiver o registro (seção 5).
|
|
129
|
+
- **`runtime.readResource(resource, id, base)`** executa a action dona com `{ id }` pelo mesmo
|
|
130
|
+
`execute` de qualquer chamada. A autorização, a auditoria, a marca de dado pessoal e o span são
|
|
131
|
+
os da action. Em caso de sucesso, devolve:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
{
|
|
135
|
+
ok: true,
|
|
136
|
+
resource: { kind: 'customer', id, title: 'Maria Souza', label: 'Assistente do cliente', skill: 'review-customer' },
|
|
137
|
+
context: { data: { ... }, truncated: false, omitted: [], personalData: true },
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Em caso de falha, devolve o erro da action, com a categoria dela. Um tipo de recurso sem action
|
|
142
|
+
dona devolve `runtime.assistant_resource_unknown`, sem executar nada.
|
|
143
|
+
|
|
144
|
+
Não há outro caminho no SDK para ler o recurso de um assistente. A declaração não exporta regra de
|
|
145
|
+
acesso, consulta nem atalho que dispense a action.
|
|
146
|
+
|
|
147
|
+
### 3. O contexto é uma projeção limitada, entregue como dado
|
|
148
|
+
|
|
149
|
+
`context.data` contém somente os caminhos declarados, com a mesma estrutura da saída. O que não foi
|
|
150
|
+
declarado não sai.
|
|
151
|
+
|
|
152
|
+
Quando o JSON serializado passa de `maxContextChars`, o runtime reduz a projeção por etapas, até
|
|
153
|
+
caber, e nunca em silêncio:
|
|
154
|
+
|
|
155
|
+
1. Remove o último item da lista que tem mais itens, e repete. Registra em `omitted` o caminho e
|
|
156
|
+
quantos itens saíram.
|
|
157
|
+
2. Com as listas vazias, encurta pela metade o texto mais longo, até o mínimo de 200 caracteres, e
|
|
158
|
+
repete. Registra o caminho em `omitted`.
|
|
159
|
+
3. Se ainda não couber, remove campos inteiros, do último declarado em `context` para o primeiro,
|
|
160
|
+
e registra cada um em `omitted`.
|
|
161
|
+
4. Marca `truncated: true`.
|
|
162
|
+
|
|
163
|
+
O teto conta só os dados de `context`. O tipo, o id e o título vão no bloco fora dessa conta, e o
|
|
164
|
+
título é sempre encurtado a no máximo 200 caracteres, com `…` e registro em `omitted`. Cada etapa
|
|
165
|
+
remove alguma coisa, e a terceira chega, no limite, a um objeto vazio, que cabe em qualquer teto
|
|
166
|
+
válido. Por isso o corte sempre termina.
|
|
167
|
+
|
|
168
|
+
Um caminho declarado pode faltar na saída em execução, porque o runtime só valida a saída em
|
|
169
|
+
desenvolvimento (`src/core/runtime.ts:907-912`). Nesse caso, a projeção omite o caminho, registra-o
|
|
170
|
+
em `omitted` com o motivo `missing`, emite um aviso no log e não falha a leitura.
|
|
171
|
+
|
|
172
|
+
A projeção é uma função pura do core, `projectAssistantContext(assistant, output)`, usada por
|
|
173
|
+
`readResource` e exportada. Um host que recebe a saída da action por outro canal, como o MCP,
|
|
174
|
+
aplica a mesma projeção sem reimplementá-la.
|
|
175
|
+
|
|
176
|
+
O host entrega a projeção ao modelo como bloco de dado delimitado, numa mensagem de dado, e nunca
|
|
177
|
+
no prompt de sistema. O bloco leva o tipo, o id, o título, os dados e, quando houver corte,
|
|
178
|
+
`truncated` e `omitted`: o modelo precisa saber que o dado está incompleto para decidir chamar a
|
|
179
|
+
action. Para hosts que usam o driver de IA do Opus, o pacote oferece a função que monta o bloco e
|
|
180
|
+
neutraliza o delimitador dentro dos valores. O host pode usar o próprio formato, desde que cumpra a
|
|
181
|
+
mesma regra.
|
|
182
|
+
|
|
183
|
+
A projeção governa só o que o host entrega **por iniciativa própria**, como o dado do recurso que
|
|
184
|
+
acompanha um turno de conversa vinculada. Quando e com que frequência entregar é decisão do host.
|
|
185
|
+
A recomendação do Opus é reaproveitar a execução que já confere o acesso, em vez de executar a
|
|
186
|
+
action de novo só para obter o contexto: cada execução de uma action com dado pessoal é uma marca
|
|
187
|
+
de exposição. Quando o modelo chama a action dona como ferramenta, recebe a saída que o
|
|
188
|
+
contrato declara, como em qualquer chamada. Filtrar a resposta pelo consumidor faria a mesma action
|
|
189
|
+
devolver coisas diferentes para a tela e para a IA. Um campo que não deve chegar ao modelo em
|
|
190
|
+
nenhuma hipótese pede outra decisão no contrato, e não uma projeção.
|
|
191
|
+
|
|
192
|
+
**Dado pessoal.** A execução da action dona é marcada no registro da requisição como qualquer
|
|
193
|
+
outra (ADR 0041). `context.personalData` repete a declaração da action para que o host marque a
|
|
194
|
+
conversa: o Opus não conhece conversas.
|
|
195
|
+
|
|
196
|
+
### 4. Projeções: manifest, ferramentas, MCP e Lente
|
|
197
|
+
|
|
198
|
+
- **Manifest.** Cada action passa a projetar `ai` normalizado (`enabled`, `description`,
|
|
199
|
+
`destructive`, `requiresConfirmation`) e, quando houver, `assistant` com todos os campos. A
|
|
200
|
+
publicação para a IA entra no manifest junto, porque sem ela a Lente não consegue dizer o que a
|
|
201
|
+
IA alcança.
|
|
202
|
+
- **`runtime.aiTools()`.** A ferramenta da action dona leva `metadata.assistant` com `resource`,
|
|
203
|
+
`skill`, `label`, `title`, `context` e `maxContextChars`. *Nota (2026-10-03):* o teto, já
|
|
204
|
+
resolvido, entrou na implementação para o host que projeta pelo MCP usar o mesmo valor; ver a
|
|
205
|
+
nota de implementação na seção 10.
|
|
206
|
+
- **MCP.** O `ListTools` acrescenta `com.softize.opus/assistant` em `_meta`, com o mesmo conteúdo.
|
|
207
|
+
O host de IA conhece o assistente pelo catálogo que já consulta, sem configuração própria.
|
|
208
|
+
- **Lente.** A estrutura de cada action passa a carregar `ai`, `effect`, `personalData` e
|
|
209
|
+
`assistant`. A página da action mostra se ela está publicada para a IA, se é dona de um tipo de
|
|
210
|
+
recurso, que procedimento o assistente segue e quais campos vão ao modelo. A mudança é feita no
|
|
211
|
+
repositório da Lente, com versão própria, lendo o manifest como hoje.
|
|
212
|
+
|
|
213
|
+
### 5. O procedimento é referência por chave, conferida pelo consumidor
|
|
214
|
+
|
|
215
|
+
`skill` é uma chave opaca para o Opus, em minúsculas com hífen (`review-customer`). O `opus check`
|
|
216
|
+
confere a forma e a presença; não confere se o procedimento existe.
|
|
217
|
+
|
|
218
|
+
Quem confere é o consumidor, onde tiver acesso às duas fontes: num teste que compare as
|
|
219
|
+
declarações (o manifest versionado ou `runtime.assistants()`) com os procedimentos que o consumidor
|
|
220
|
+
versiona, ou no turno, quando o host já tem o catálogo e o registro em mãos. A subida de um serviço
|
|
221
|
+
não é um bom lugar: as duas fontes podem estar em serviços diferentes, e o catálogo pode exigir
|
|
222
|
+
credencial. Na Grand Brasil, as declarações vivem no serviço principal, as Habilidades no serviço
|
|
223
|
+
de IA, e o MCP interno só lista ferramentas com sessão e credencial de turno
|
|
224
|
+
(`packages/agent-authority/src/staff-mcp.ts:95-109` dela).
|
|
225
|
+
|
|
226
|
+
A Lente mostra a chave declarada. Mostrar se a chave resolve exige que o host informe esse estado
|
|
227
|
+
à Lente, o que fica fora desta decisão.
|
|
228
|
+
|
|
229
|
+
### 6. Acesso a recurso só por action
|
|
230
|
+
|
|
231
|
+
Para o Opus, o princípio é: o recurso de um assistente é lido, e o acesso a ele é conferido,
|
|
232
|
+
somente pela action dona. O SDK cumpre a sua parte nas seções 2 e 3.
|
|
233
|
+
|
|
234
|
+
O Opus não enxerga as rotas HTTP que o consumidor registra por conta própria. O teste que impede
|
|
235
|
+
uma rota de conferir acesso sem action é do consumidor. O `docs/protocol.md` registra o princípio e
|
|
236
|
+
recomenda esse teste.
|
|
237
|
+
|
|
238
|
+
### 7. O que o `opus check`, o `opus gen` e o `opus copy` cobram
|
|
239
|
+
|
|
240
|
+
A regra `assistant-declaration` do `opus check` reprova:
|
|
241
|
+
|
|
242
|
+
- `assistant` em action que não é `view`;
|
|
243
|
+
- `assistant` com `ai: false` ou sem `ai`;
|
|
244
|
+
- `assistant` sem `personalData` declarado;
|
|
245
|
+
- `resource` fora de camelCase ou `skill` fora de minúsculas com hífen;
|
|
246
|
+
- `label`, `title` ou `context` ausentes;
|
|
247
|
+
- o mesmo `resource` em dois contratos, pelo valor literal, entre arquivos.
|
|
248
|
+
|
|
249
|
+
O `opus gen`, que já converte a saída em JSON Schema (`bin/lib/gen-runner.mjs:399`), recusa gerar
|
|
250
|
+
o manifest quando `title` ou um caminho de `context` não existem na saída, ou quando a entrada não
|
|
251
|
+
tem `id` de texto como único campo obrigatório. Num campo anulável ou numa união (`anyOf` no JSON
|
|
252
|
+
Schema), o caminho vale se existir em algum ramo que não seja `null`.
|
|
253
|
+
|
|
254
|
+
O `register` do runtime recusa duas actions donas do mesmo `resource`, com
|
|
255
|
+
`runtime.assistant_duplicate_resource`, e um `maxContextChars` fora do intervalo, com
|
|
256
|
+
`runtime.assistant_context_limit_invalid`. Os erros desta decisão usam o prefixo
|
|
257
|
+
`runtime.assistant_`.
|
|
258
|
+
|
|
259
|
+
O `opus copy` passa a ler `assistant.label`.
|
|
260
|
+
|
|
261
|
+
### 8. Relação com a Presentation
|
|
262
|
+
|
|
263
|
+
`assistant.triggerLabel` da Presentation continua sendo a capacidade de interface: o botão no
|
|
264
|
+
header e a divisão da superfície. A declaração da action é o comportamento do chat. As duas são
|
|
265
|
+
independentes nesta decisão: o assistente existe também onde não há Presentation, como num drawer
|
|
266
|
+
composto à mão ou no chat geral.
|
|
267
|
+
|
|
268
|
+
### 9. O que fica fora
|
|
269
|
+
|
|
270
|
+
- Endereço de navegação do recurso. É da interface do consumidor.
|
|
271
|
+
- Persona, regras próprias de cada tipo e a forma de ativar o procedimento no turno. São do host.
|
|
272
|
+
- Escrita. A declaração descreve a leitura; a escrita continua governada por `effect`, `ai` e pelo
|
|
273
|
+
host.
|
|
274
|
+
|
|
275
|
+
### 10. As questões que ficaram para decisão
|
|
276
|
+
|
|
277
|
+
A proposta deixou cinco questões para decidir antes da implementação. Em 2026-10-03, todas foram
|
|
278
|
+
decididas como a proposta recomendava; cada item registra a questão, a decisão e o motivo.
|
|
279
|
+
|
|
280
|
+
1. **Nomes finais.** *Decisão:* `assistant`, `resource`, `skill`, `label`, `title`, `context` e
|
|
281
|
+
`runtime.readResource`. `skill` acompanha o identificador técnico que os consumidores já usam;
|
|
282
|
+
para pessoas, a Grand Brasil continua dizendo Habilidade.
|
|
283
|
+
2. **Teto do contexto.** *Decisão:* 4.000 caracteres por padrão, 1.000 no mínimo e 16.000 no
|
|
284
|
+
máximo (seções 1 e 3). Cabe o resumo de um cadastro e fica longe do teto de histórico que a
|
|
285
|
+
Grand Brasil usa (40.000).
|
|
286
|
+
3. **Projeção nas respostas de ferramenta.** *Decisão:* não aplicar, pelos motivos da seção 3.
|
|
287
|
+
4. **Rótulo do botão da Presentation.** *Decisão:* `triggerLabel` e `assistant.label` ficam
|
|
288
|
+
independentes (seção 8). Herdar o rótulo da action do corpo na Presentation, caso a repetição
|
|
289
|
+
apareça na prática, pede uma decisão própria.
|
|
290
|
+
5. **Versão.** *Decisão:* minor, porque o campo é opcional e a regra só cobra quem declara.
|
|
291
|
+
|
|
292
|
+
**Nota de implementação (2026-10-03).** A implementação preencheu os pontos que a decisão deixava em
|
|
293
|
+
aberto assim:
|
|
294
|
+
|
|
295
|
+
- **Projeção sob pedido.** `runtime.readResource` ganhou um quarto parâmetro opcional,
|
|
296
|
+
`{ context?: boolean }`. Sem ele, a leitura devolve só o descritor, que é o que a listagem de um
|
|
297
|
+
histórico usa, e resolve apenas o título (`resolveAssistantTitle`), sem serializar nem cortar a
|
|
298
|
+
saída; com `context: true`, devolve também a projeção. A execução da action e a autorização são as
|
|
299
|
+
mesmas nos dois casos. O aviso de título ausente vale sempre; o de caminho de `context` ausente,
|
|
300
|
+
só quando a projeção roda.
|
|
301
|
+
- **Falha na projeção.** Uma saída que o JSON não representa (`BigInt`, referência circular) não
|
|
302
|
+
derruba a leitura com exceção: `readResource` registra o erro no log e devolve
|
|
303
|
+
`runtime.assistant_projection_failed` (categoria `internal`). A execução da action já foi
|
|
304
|
+
auditada.
|
|
305
|
+
- **Tipo sem dona.** `runtime.assistant_resource_unknown` tem mensagem fixa; o tipo recebido vai em
|
|
306
|
+
`meta.resource`, para não ecoar entrada de fora no texto do erro.
|
|
307
|
+
- **Registro.** Além do dono duplicado e do teto, o `register` recusa a forma inválida da
|
|
308
|
+
declaração com `runtime.assistant_declaration_invalid`: `assistant` fora de `view`, `ai` não
|
|
309
|
+
habilitado, `personalData` não declarado, `resource` ou `skill` fora do formato, `label`, `title`
|
|
310
|
+
ou `context` ausentes ou caminho com segmento vazio. Quem não roda o `opus check` no gate recebe a
|
|
311
|
+
mesma régua ao subir o serviço. A entrada `{ id }` continua conferida só pelo `opus gen`, que
|
|
312
|
+
enxerga o JSON Schema; o core segue sem dependência de schema.
|
|
313
|
+
- **Teto nas projeções.** `aiTools()`, o `_meta` do MCP, o manifest e `runtime.assistants()` levam
|
|
314
|
+
a mesma forma, com `maxContextChars` já resolvido (4.000 quando não declarado), para o host que
|
|
315
|
+
aplica `projectAssistantContext` por outro canal usar o teto declarado. O `opus check` também reprova
|
|
316
|
+
`maxContextChars` literal fora de 1.000 a 16.000; um valor não literal fica para o registro.
|
|
317
|
+
- **`omitted`.** Cada entrada é `{ path, reason, count? }`, com `reason` igual a `items` (itens
|
|
318
|
+
removidos do fim da lista, `count` diz quantos), `shortened`, `removed` ou `missing`. O caminho é
|
|
319
|
+
o da saída, sem índice de lista: em `removed` e `missing`, é o declarado (ou o do título); em
|
|
320
|
+
`items` e `shortened`, pode ficar abaixo do declarado quando o caminho declarado termina num
|
|
321
|
+
objeto ou numa lista. Um título ausente, nulo ou que não é texto nem número vira `title: null` e
|
|
322
|
+
entra em `omitted` como `missing`, com o aviso no log. O título passa por `toJSON` como na
|
|
323
|
+
serialização, para sair igual com e sem a projeção (uma data vira o texto ISO nos dois casos). O `opus gen` recusa `title` que não
|
|
324
|
+
resolve para texto ou número, fora de lista, em algum ramo não nulo.
|
|
325
|
+
- **Custo do corte.** As etapas 1 e 2 escolhem a maior lista e o texto mais longo por fila de
|
|
326
|
+
prioridade, sem varrer a projeção a cada passo: o custo cresce com o tamanho da saída. Um teste
|
|
327
|
+
roda os cenários num processo filho com prazo, porque o laço é síncrono e o prazo do teste não o
|
|
328
|
+
interromperia.
|
|
329
|
+
- **Segmentos reservados.** `__proto__`, `constructor` e `prototype` não são aceitos como segmento
|
|
330
|
+
de `title` ou `context` no registro, no `opus gen` e, quando literais, no `opus check`; a projeção
|
|
331
|
+
grava as chaves como propriedades próprias.
|
|
332
|
+
- **Caminhos sobrepostos.** Um caminho coberto por outro declarado, como `leads.totalCount` junto de
|
|
333
|
+
`leads`, é ignorado na projeção, porque o mais curto já leva o valor inteiro.
|
|
334
|
+
- **Bloco ao modelo.** A função é `resourceDataBlock`, de `@softize/opus/ai`. O bloco é uma linha de
|
|
335
|
+
JSON entre `<resource-data>` e `</resource-data>` (a etiqueta pode ser trocada por outra em
|
|
336
|
+
minúsculas com hífen). Saem como escapes `\u`, que o JSON lê como o mesmo texto: `<` e `>`; os
|
|
337
|
+
sinais que a normalização NFKC transforma neles; os confusáveis cujo esqueleto contém `<` ou `>`,
|
|
338
|
+
derivados do confusables.txt do Unicode (UTS #39, versão de 2026-08-06); os colchetes angulares
|
|
339
|
+
de outros blocos; os caracteres de controle (C0, DEL, C1 e NEL); os de formatação
|
|
340
|
+
invisíveis; os seletores de variação; os preenchimentos do hangul; os separadores de linha e
|
|
341
|
+
parágrafo; e os espaços que não são o espaço comum. `truncated` e `omitted` entram quando houve
|
|
342
|
+
corte ou quando faltou algum caminho.
|
|
343
|
+
- **`opus gen`.** Um campo sem forma declarada na saída (`z.unknown()`, `z.any()`) aceita qualquer
|
|
344
|
+
caminho abaixo dele, porque não há o que conferir. Um schema que não pôde ser convertido em JSON
|
|
345
|
+
Schema recusa o manifest. Na entrada, além de `id`, campos opcionais sem valor padrão também são
|
|
346
|
+
aceitos: a decisão só recusa outro campo obrigatório.
|
|
347
|
+
- **`label`.** É texto simples, não `I18nRef`, como o `triggerLabel` da Presentation. O `opus copy`
|
|
348
|
+
o inventaria com o papel `title`, porque ele nomeia o assistente na superfície do chat.
|
|
349
|
+
- **Aviso de caminho ausente.** O log usa o código `runtime.assistant_path_missing`, com a action,
|
|
350
|
+
o recurso e os caminhos.
|
|
351
|
+
- **Caminho que termina num contêiner.** Um caminho de `context` que termina num objeto ou numa
|
|
352
|
+
lista projeta tudo o que está abaixo dele, inclusive campos que a saída ganhar depois. O consumidor
|
|
353
|
+
que mantém uma lista de campos proibidos precisa conferir os descendentes, e não só o último
|
|
354
|
+
segmento do caminho. Para isso, `@softize/opus/assistant-schema` exporta
|
|
355
|
+
`assistantContextLeaves(output, context)`: a partir do JSON Schema da saída, como o manifest o
|
|
356
|
+
traz, devolve por caminho os campos folha alcançados e os pontos sem forma declarada ou em que um
|
|
357
|
+
`$ref` volta a si mesmo, por onde qualquer campo pode passar. O subpath publica tipos
|
|
358
|
+
(`assistant-schema.d.mts`) para um teste em TypeScript estrito.
|
|
359
|
+
|
|
360
|
+
## Consequências
|
|
361
|
+
|
|
362
|
+
- A action dona passa a ser a fonte única de leitura, acesso, procedimento e dado ao modelo para um
|
|
363
|
+
tipo de recurso. O consumidor troca tabelas no host, montagem na tela e `if` por tipo por uma
|
|
364
|
+
declaração e duas leituras do runtime.
|
|
365
|
+
- O manifest cresce com `ai` e `assistant`. Projetos que versionam o manifest regeneram com
|
|
366
|
+
`opus gen` na atualização.
|
|
367
|
+
- A Lente passa a mostrar, por action, o que a IA alcança e o que cada assistente entrega ao
|
|
368
|
+
modelo. Depende de uma versão nova da Lente.
|
|
369
|
+
- O campo é opcional e entra numa minor. A regra do `opus check` vale só para quem declara.
|
|
370
|
+
- A conferência de acesso executa a action inteira. Uma action dona pesada fica cara para listar
|
|
371
|
+
conversas; o consumidor mede e, se preciso, escolhe uma `view` mais leve como dona.
|
|
372
|
+
- A existência do procedimento continua fora do alcance do SDK. Uma chave errada só aparece no
|
|
373
|
+
teste do consumidor ou no turno.
|
|
374
|
+
|
|
375
|
+
## Alternativas descartadas
|
|
376
|
+
|
|
377
|
+
### Manter a política no host
|
|
378
|
+
|
|
379
|
+
É o estado atual. Cada consumidor repete tabela de procedimentos, montagem de contexto e rota por
|
|
380
|
+
tipo, e nada disso aparece no manifest nem na Lente.
|
|
381
|
+
|
|
382
|
+
### Declarar dentro de `ai`
|
|
383
|
+
|
|
384
|
+
`ai: { assistant: { ... } }` deixaria tudo que é de IA num lugar. Mas a declaração também define a
|
|
385
|
+
action dona do recurso, usada para conferir acesso em telas e listagens que não são da IA, como o
|
|
386
|
+
histórico de conversas. É o mesmo motivo que levou `effect` e `personalData` para fora de `ai` na
|
|
387
|
+
ADR 0041.
|
|
388
|
+
|
|
389
|
+
### Declarar na Presentation
|
|
390
|
+
|
|
391
|
+
A Presentation é interface, e o recurso precisa do assistente também onde não há Presentation: num
|
|
392
|
+
drawer composto pelo consumidor ou no chat geral, que não tem superfície.
|
|
393
|
+
|
|
394
|
+
### Projeção por função
|
|
395
|
+
|
|
396
|
+
`context: (output) => ...` seria mais flexível, mas não é serializável. Sem uma lista de caminhos,
|
|
397
|
+
o manifest e a Lente não conseguem mostrar quais dados vão ao modelo, que é o objetivo da
|
|
398
|
+
declaração.
|
|
399
|
+
|
|
400
|
+
### Validar o procedimento no `opus check`
|
|
401
|
+
|
|
402
|
+
O check é estático e o registro de procedimentos é dado de banco, diferente em cada ambiente.
|
|
403
|
+
Reprovar ali exigiria acesso ao banco no gate ou uma cópia do registro no repositório.
|
|
404
|
+
|
|
405
|
+
### Aplicar a projeção também às respostas de ferramenta
|
|
406
|
+
|
|
407
|
+
Minimizaria o dado ao modelo, mas a mesma action devolveria coisas diferentes por consumidor, e os
|
|
408
|
+
procedimentos que precisam da saída completa, como editar um relatório pela revisão lida,
|
|
409
|
+
deixariam de funcionar.
|
|
410
|
+
|
|
411
|
+
## Verificação
|
|
412
|
+
|
|
413
|
+
- Testes do SDK:
|
|
414
|
+
- `assistant` numa action que não é `view`, sem `ai` ou sem `personalData` falha no `opus check`;
|
|
415
|
+
- dois contratos com o mesmo `resource` falham no `opus check` e no `register`;
|
|
416
|
+
- `opus gen` recusa `title` ou caminho de `context` ausentes na saída, aceita caminho que existe
|
|
417
|
+
num ramo não nulo de `anyOf` e recusa entrada sem `id` ou com outro campo obrigatório;
|
|
418
|
+
- `readResource` executa a action dona pelo `execute`: a autorização da action vale, o registro
|
|
419
|
+
de auditoria é gerado e a marca de dado pessoal aparece;
|
|
420
|
+
- `readResource` com tipo sem action dona devolve `runtime.assistant_resource_unknown` sem
|
|
421
|
+
executar nada;
|
|
422
|
+
- a projeção devolve só os caminhos declarados, inclusive dentro de listas, e `null` quando um
|
|
423
|
+
campo intermediário é nulo;
|
|
424
|
+
- um caminho ausente em execução é omitido com motivo `missing` e aviso no log, sem falhar;
|
|
425
|
+
- acima do teto, a projeção segue as etapas da seção 3 na ordem, marca `truncated`, lista o que
|
|
426
|
+
saiu em `omitted` e termina mesmo com o teto mínimo;
|
|
427
|
+
- um título acima de 200 caracteres é encurtado e registrado em `omitted`, sem entrar na conta do
|
|
428
|
+
teto;
|
|
429
|
+
- o bloco entregue ao modelo leva `truncated` e `omitted`, e a função de bloco neutraliza o
|
|
430
|
+
delimitador dentro dos valores;
|
|
431
|
+
- o manifest projeta `ai` e `assistant`; `aiTools()` e o `ListTools` do MCP levam a declaração;
|
|
432
|
+
- `opus copy` inventaria `assistant.label`.
|
|
433
|
+
- Documentação e skills do pacote, no mesmo entregável:
|
|
434
|
+
- `docs/protocol.md` ganha o campo na definição de action (seção 2), o princípio da seção 6 desta
|
|
435
|
+
ADR e o verbete no glossário;
|
|
436
|
+
- a página de actions da documentação (`src/ui/docs/content/actions.md`) mostra o campo e a ordem
|
|
437
|
+
canônica com ele;
|
|
438
|
+
- as skills `create-opus-action` e `test-opus-action` (`registry/skills`) ensinam a declarar e a
|
|
439
|
+
testar o assistente.
|
|
440
|
+
- Na Lente: a página de uma action com assistente mostra publicação para a IA, efeito, dado pessoal,
|
|
441
|
+
procedimento e campos enviados ao modelo.
|
|
442
|
+
- Na Grand Brasil, a adoção (ADR 0111 dela) declara as quatro actions donas e troca a rota por tipo
|
|
443
|
+
pela leitura do runtime.
|
package/docs/protocol.md
CHANGED
|
@@ -135,6 +135,16 @@ type ViewAction<In, Out> = ActionBase<In, Out> & {
|
|
|
135
135
|
kind: 'view'
|
|
136
136
|
projection: string[]
|
|
137
137
|
expand?: Record<string, ExpandSpec>
|
|
138
|
+
assistant?: ActionAssistant // torna a view a action dona do recurso (ADR 0053)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
type ActionAssistant = {
|
|
142
|
+
resource: string // tipo de recurso, camelCase, único no runtime
|
|
143
|
+
skill: string // chave do procedimento da IA, minúsculas com hífen
|
|
144
|
+
label: string // nome do assistente para pessoas
|
|
145
|
+
title: string // caminho, na saída, do campo que nomeia o recurso
|
|
146
|
+
context: string[] // caminhos, na saída, do dado que pode ir ao modelo
|
|
147
|
+
maxContextChars?: number // teto do JSON de context: padrão 4.000, de 1.000 a 16.000
|
|
138
148
|
}
|
|
139
149
|
```
|
|
140
150
|
|
|
@@ -230,6 +240,47 @@ Output de search action é **sempre** `Paginated<Output>` (ver §6).
|
|
|
230
240
|
**`view`**
|
|
231
241
|
- `projection: string[]` — campos/relações sempre carregados (`['owner', 'company.address']`).
|
|
232
242
|
- `expand?: Record<string, ExpandSpec>` — relações opt-in por request.
|
|
243
|
+
- `assistant?: ActionAssistant` — declara o assistente do recurso que a view lê e a torna a
|
|
244
|
+
**action dona** desse tipo de recurso (ADR 0053). Ver "Assistente do recurso", abaixo.
|
|
245
|
+
|
|
246
|
+
### Assistente do recurso
|
|
247
|
+
|
|
248
|
+
Uma conversa sobre um recurso específico, como um cliente ou um relatório, precisa ler o recurso
|
|
249
|
+
com a permissão de quem conversa, seguir o procedimento próprio daquele tipo e entregar ao modelo só
|
|
250
|
+
o dado necessário. A action `view` que lê o recurso declara as três coisas em `assistant` (ADR 0053).
|
|
251
|
+
|
|
252
|
+
- **Requisitos da action dona.** Ser `view`; ter `ai` habilitado; declarar `personalData`; ter
|
|
253
|
+
entrada em que `id`, do tipo texto, é o único campo obrigatório. Cada `resource` tem uma única
|
|
254
|
+
action dona no runtime. O registro recusa a forma inválida (`runtime.assistant_declaration_invalid`),
|
|
255
|
+
o teto fora do intervalo (`runtime.assistant_context_limit_invalid`) e o dono duplicado
|
|
256
|
+
(`runtime.assistant_duplicate_resource`); o `opus check` (regra `assistant-declaration`) e o
|
|
257
|
+
`opus gen` (caminhos e entrada contra o JSON Schema) cobram o mesmo antes da execução.
|
|
258
|
+
- **Caminhos.** O ponto desce em objetos; um caminho que passa por uma lista vale para cada item;
|
|
259
|
+
um intermediário `null` aparece como `null`. Num campo anulável ou numa união, o `opus gen` aceita
|
|
260
|
+
o caminho que existe em algum ramo não nulo. Um caminho que termina num objeto ou numa lista leva
|
|
261
|
+
tudo abaixo dele (`assistantContextLeaves`, de `@softize/opus/assistant-schema`, lista o que cada
|
|
262
|
+
caminho alcança). `title` aponta para texto ou número, fora de lista.
|
|
263
|
+
- **Leitura.** `runtime.readResource(resource, id, base, { context? })` executa a action dona com
|
|
264
|
+
`{ id }` pelo `execute`: autorização, loaders, auditoria, marca de dado pessoal e span são os da
|
|
265
|
+
action. Devolve `{ ok: true, resource: { kind, id, title, label, skill }, context?, meta }` ou o
|
|
266
|
+
erro da action; um tipo sem dona devolve `runtime.assistant_resource_unknown` sem executar nada.
|
|
267
|
+
`runtime.assistants()` lista as declarações com a action dona.
|
|
268
|
+
- **Projeção.** `projectAssistantContext(assistant, output)`, função pura do core, devolve só os
|
|
269
|
+
caminhos declarados. Acima do teto, corta por etapas até caber: o último item da maior lista; com
|
|
270
|
+
as listas vazias, o texto mais longo pela metade, até 200 caracteres; por fim, campos inteiros, do
|
|
271
|
+
último declarado ao primeiro. Marca `truncated` e registra o que saiu em `omitted`. O título fica
|
|
272
|
+
fora do teto e é encurtado a 200 caracteres. Caminho ausente na saída vai a `omitted` com o motivo
|
|
273
|
+
`missing`, sem falhar a leitura.
|
|
274
|
+
- **Entrega ao modelo.** O host entrega a projeção como bloco de dado delimitado, numa mensagem de
|
|
275
|
+
dado, nunca no prompt de sistema (`resourceDataBlock`, de `@softize/opus/ai`). A projeção governa
|
|
276
|
+
só o que o host entrega por iniciativa própria; quando o modelo chama a action dona como
|
|
277
|
+
ferramenta, recebe a saída completa do contrato.
|
|
278
|
+
|
|
279
|
+
**Princípio: acesso a recurso só por action.** O recurso de um assistente é lido, e o acesso a ele é
|
|
280
|
+
conferido, somente pela action dona. O SDK não oferece outro caminho: a declaração não exporta
|
|
281
|
+
regra de acesso, consulta nem atalho que dispense a action. O Opus não enxerga as rotas HTTP que o
|
|
282
|
+
consumidor registra por conta própria; recomenda-se que o consumidor mantenha um teste que reprove
|
|
283
|
+
rota que leia tabela de recurso ou chame regra de acesso de domínio fora de uma action.
|
|
233
284
|
|
|
234
285
|
### Exemplos por kind
|
|
235
286
|
|
|
@@ -2279,6 +2330,9 @@ chamada, inclusive por IA e MCP.
|
|
|
2279
2330
|
- **`isSystemActor(ctx)`** — responde se a execução foi criada pelo runtime para o sistema (schedule ou job que ele enfileirou). Usado no `authorize` de actions `automatable`. Ver §4.
|
|
2280
2331
|
- **`internal`** — flag que indica action service-to-service apenas; server adapter não expõe como REST público. Ver §2.
|
|
2281
2332
|
- **`effect`** — o que a action faz com o estado: `read` ou `write`. `list` e `view` são leitura pelo `GET` e não declaram; `simple` e `form` declaram. Projetado no manifest, nas ferramentas de IA e no MCP (`readOnlyHint`). Ver ADR 0041.
|
|
2333
|
+
- **`assistant`** — declaração, numa action `view`, do assistente do recurso que ela lê: tipo de recurso, chave do procedimento, nome, campo do título e caminhos do dado ao modelo. Ver §2 e ADR 0053.
|
|
2334
|
+
- **Action dona** — a única action `view` que declara `assistant` para um tipo de recurso; é por ela que o recurso é lido e que o acesso a ele é conferido. Ver §2.
|
|
2335
|
+
- **`runtime.readResource`** — lê um recurso pela action dona, pelo mesmo `execute` de qualquer chamada, e devolve o descritor e, quando pedida, a projeção limitada do dado ao modelo. Ver §2.
|
|
2282
2336
|
- **`personalData`** — a resposta contém dados que identificam pessoas. Com `true`, toda execução bem-sucedida é marcada no `AuditRecord` (`personalData.records`) e no span, qualquer que seja o consumidor. Ver §5 e ADR 0041.
|
|
2283
2337
|
- **Reaction** — declaração que escuta evento(s) e executa handler. Auto-registrada pelo runtime via EventBusAdapter. Pareada com Action (ator inicia × sistema reage). Ver §12.
|
|
2284
2338
|
- **`defineReaction`** — função que constrói uma reaction declarativa.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softize/opus",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.2.0",
|
|
4
4
|
"description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"./introspect": {
|
|
21
21
|
"default": "./bin/lib/introspect.mjs"
|
|
22
22
|
},
|
|
23
|
+
"./assistant-schema": {
|
|
24
|
+
"types": "./bin/lib/assistant-schema.d.mts",
|
|
25
|
+
"default": "./bin/lib/assistant-schema.mjs"
|
|
26
|
+
},
|
|
23
27
|
"./init": {
|
|
24
28
|
"default": "./bin/lib/init.mjs"
|
|
25
29
|
},
|
|
@@ -16,6 +16,7 @@ registrados no runtime do projeto.
|
|
|
16
16
|
- nome `<resource>.<verb>` e `kind` (`simple`, `form`, `list` ou `view`);
|
|
17
17
|
- comportamento, autorização, input, output, erros e efeitos esperados;
|
|
18
18
|
- se a action só lê ou altera estado e se a resposta contém dados que identificam pessoas;
|
|
19
|
+
- se a action `view` é a leitura de um tipo de recurso com conversa própria da IA (assistente);
|
|
19
20
|
- caminhos reais de `shared` e `api` adotados pelo repo.
|
|
20
21
|
|
|
21
22
|
## Procedimento
|
|
@@ -28,13 +29,17 @@ registrados no runtime do projeto.
|
|
|
28
29
|
`effect: 'write'` e deixa `personalData` comentado de propósito: a ausência aparece no aviso do
|
|
29
30
|
`opus check` até alguém decidir, porque um `false` preenchido por padrão esconderia exposição de
|
|
30
31
|
dado pessoal.
|
|
31
|
-
6.
|
|
32
|
-
|
|
32
|
+
6. Quando a `view` for a action dona de um tipo de recurso com assistente, declarar `assistant`
|
|
33
|
+
depois de `ai`, com `ai` habilitado, `personalData` declarado e entrada `{ id }`, conforme
|
|
34
|
+
[contrato e binding](references/contract-and-binding.md).
|
|
35
|
+
7. Exportar e registrar o binding no domínio/runtime existente.
|
|
36
|
+
8. Carregar e seguir `$test-opus-action` para cobrir contrato, sucesso, erros e efeitos relevantes.
|
|
33
37
|
|
|
34
38
|
## Verificação
|
|
35
39
|
|
|
36
40
|
Rodar `opus check <escopo>`, typecheck e testes do domínio. Se o projeto gera manifest,
|
|
37
|
-
OpenAPI ou docs, regenerar e conferir o diff
|
|
41
|
+
OpenAPI ou docs, regenerar e conferir o diff; com `assistant`, o `opus gen` confere `title`,
|
|
42
|
+
`context` e a entrada contra os schemas. Regenerar também o inventário com `opus copy`
|
|
38
43
|
e executar `base copy check` quando o contrato declarar texto humano.
|
|
39
44
|
|
|
40
45
|
## Limites
|