@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/src/core/runtime.ts
CHANGED
|
@@ -25,6 +25,17 @@ import { createPurgeHandler, entityTableName, type PurgeHandler } from './retent
|
|
|
25
25
|
import type { DataProductConfig } from './data-product.ts'
|
|
26
26
|
import { AuditEmitter } from './audit.ts'
|
|
27
27
|
import { countReturnedRecords, hasInvalidEffect, resolveActionEffect } from './action-nature.ts'
|
|
28
|
+
import {
|
|
29
|
+
assertAssistantDeclaration,
|
|
30
|
+
projectAssistantContext,
|
|
31
|
+
publicAssistant,
|
|
32
|
+
resolveAssistantTitle,
|
|
33
|
+
type AssistantOmission,
|
|
34
|
+
type ReadResourceOptions,
|
|
35
|
+
type ResourceContext,
|
|
36
|
+
type RegisteredAssistant,
|
|
37
|
+
type ResourceReadResult,
|
|
38
|
+
} from './assistant.ts'
|
|
28
39
|
import { error, isActionError, normalizeError } from './errors.ts'
|
|
29
40
|
import { normalizeTraceContext } from './trace.ts'
|
|
30
41
|
import { isSystemActor, markSystemActor } from './system-actor.ts'
|
|
@@ -78,6 +89,7 @@ import type {
|
|
|
78
89
|
BoundAi,
|
|
79
90
|
AiTool,
|
|
80
91
|
AIConfig,
|
|
92
|
+
ActionAssistant,
|
|
81
93
|
User,
|
|
82
94
|
TraceContext,
|
|
83
95
|
} from './types.ts'
|
|
@@ -321,6 +333,8 @@ export class Runtime {
|
|
|
321
333
|
private aiToolsCache: AiTool[] | null = null
|
|
322
334
|
|
|
323
335
|
private readonly actions = new Map<string, ActionDef>()
|
|
336
|
+
/** A action dona de cada tipo de recurso com assistente (ADR 0053). */
|
|
337
|
+
private readonly assistantOwners = new Map<string, ActionDef & { assistant: ActionAssistant }>()
|
|
324
338
|
private readonly reactions = new Map<string, ReactionDef<any>>()
|
|
325
339
|
private readonly schedules = new Map<string, ScheduleDef>()
|
|
326
340
|
private readonly dataProducts = new Map<string, DataProductConfig>()
|
|
@@ -532,11 +546,33 @@ export class Runtime {
|
|
|
532
546
|
'Use simple ou form para escrever (ADR 0041).',
|
|
533
547
|
})
|
|
534
548
|
}
|
|
549
|
+
this.claimAssistant(action)
|
|
535
550
|
this.actions.set(action.name, action)
|
|
536
551
|
if (this.config.dev) this.warnIgnoredExecutionFields(action)
|
|
537
552
|
}
|
|
538
553
|
}
|
|
539
554
|
|
|
555
|
+
/**
|
|
556
|
+
* Valida a declaração do assistente e reserva o tipo de recurso para a action (ADR 0053). Duas
|
|
557
|
+
* actions donas do mesmo recurso são recusadas com `runtime.assistant_duplicate_resource`.
|
|
558
|
+
*/
|
|
559
|
+
private claimAssistant(action: ActionDef): void {
|
|
560
|
+
assertAssistantDeclaration(action)
|
|
561
|
+
const assistant = (action as { assistant?: ActionAssistant }).assistant
|
|
562
|
+
if (assistant === undefined) return
|
|
563
|
+
const owner = this.assistantOwners.get(assistant.resource)
|
|
564
|
+
if (owner !== undefined) {
|
|
565
|
+
throw error({
|
|
566
|
+
code: 'runtime.assistant_duplicate_resource',
|
|
567
|
+
category: 'internal',
|
|
568
|
+
message:
|
|
569
|
+
`O recurso "${assistant.resource}" já tem a action dona "${owner.name}"; ` +
|
|
570
|
+
`"${action.name}" não pode declarar o mesmo resource (ADR 0053).`,
|
|
571
|
+
})
|
|
572
|
+
}
|
|
573
|
+
this.assistantOwners.set(assistant.resource, action as ActionDef & { assistant: ActionAssistant })
|
|
574
|
+
}
|
|
575
|
+
|
|
540
576
|
/**
|
|
541
577
|
* `emits` e `background` pertencem ao binding de qualquer kind pelo tipo, mas o runtime e o
|
|
542
578
|
* manifest só os aplicam em `simple` e `form`. Restringir o tipo quebraria bindings
|
|
@@ -726,6 +762,103 @@ export class Runtime {
|
|
|
726
762
|
return this.executeAction(action, input, ctxBase, 'request')
|
|
727
763
|
}
|
|
728
764
|
|
|
765
|
+
/**
|
|
766
|
+
* Lê um recurso pela action dona do tipo (ADR 0053). Executa a action com `{ id }` pelo mesmo
|
|
767
|
+
* `execute` de qualquer chamada: autorização, loaders, auditoria, marca de dado pessoal e span são
|
|
768
|
+
* os da action. Em caso de sucesso, devolve o descritor e, com `{ context: true }`, a projeção
|
|
769
|
+
* limitada que o host pode entregar ao modelo. Em caso de falha, devolve o erro da action. Um tipo
|
|
770
|
+
* sem action dona devolve `runtime.assistant_resource_unknown`, sem executar nada.
|
|
771
|
+
*/
|
|
772
|
+
async readResource(
|
|
773
|
+
resource: string,
|
|
774
|
+
id: string,
|
|
775
|
+
ctxBase: ContextBase,
|
|
776
|
+
options: ReadResourceOptions = {},
|
|
777
|
+
): Promise<ResourceReadResult> {
|
|
778
|
+
const owner = this.assistantOwners.get(resource)
|
|
779
|
+
if (owner === undefined) {
|
|
780
|
+
return {
|
|
781
|
+
ok: false,
|
|
782
|
+
error: error({
|
|
783
|
+
code: 'runtime.assistant_resource_unknown',
|
|
784
|
+
category: 'validation',
|
|
785
|
+
// O tipo veio de fora: a mensagem é fixa, e o valor recebido fica em `meta`.
|
|
786
|
+
message: 'O tipo de recurso informado não tem action dona.',
|
|
787
|
+
meta: { resource },
|
|
788
|
+
}),
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const result = await this.execute(owner.name, { id }, ctxBase)
|
|
792
|
+
if (!result.ok) return { ok: false, error: result.error, meta: result.meta }
|
|
793
|
+
|
|
794
|
+
const { assistant } = owner
|
|
795
|
+
let title: string | null
|
|
796
|
+
let omitted: AssistantOmission[]
|
|
797
|
+
let context: ResourceContext | undefined
|
|
798
|
+
try {
|
|
799
|
+
if (options.context === true) {
|
|
800
|
+
// A projeção já resolve o título; sem contexto pedido, basta o título, sem pagar o corte.
|
|
801
|
+
const projection = projectAssistantContext(assistant, result.data)
|
|
802
|
+
title = projection.title
|
|
803
|
+
omitted = projection.omitted
|
|
804
|
+
context = {
|
|
805
|
+
data: projection.data,
|
|
806
|
+
truncated: projection.truncated,
|
|
807
|
+
omitted: projection.omitted,
|
|
808
|
+
personalData: owner.personalData === true,
|
|
809
|
+
}
|
|
810
|
+
} else {
|
|
811
|
+
;({ title, omitted } = resolveAssistantTitle(assistant, result.data))
|
|
812
|
+
}
|
|
813
|
+
} catch (thrown) {
|
|
814
|
+
// A action executou e foi auditada; o que falhou foi transformar a saída em dado ao modelo
|
|
815
|
+
// (por exemplo, `BigInt` ou referência circular, que o JSON não representa).
|
|
816
|
+
this.log.error('A saída da action dona não pôde ser projetada para o assistente.', {
|
|
817
|
+
code: 'runtime.assistant_projection_failed',
|
|
818
|
+
action: owner.name,
|
|
819
|
+
resource,
|
|
820
|
+
error: thrown instanceof Error ? thrown.message : String(thrown),
|
|
821
|
+
})
|
|
822
|
+
return {
|
|
823
|
+
ok: false,
|
|
824
|
+
error: error({
|
|
825
|
+
code: 'runtime.assistant_projection_failed',
|
|
826
|
+
category: 'internal',
|
|
827
|
+
message: 'Não foi possível preparar os dados deste recurso para o assistente.',
|
|
828
|
+
cause: thrown,
|
|
829
|
+
}),
|
|
830
|
+
meta: result.meta,
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const missing = omitted.filter((entry) => entry.reason === 'missing').map((entry) => entry.path)
|
|
834
|
+
if (missing.length > 0) {
|
|
835
|
+
this.log.warn(
|
|
836
|
+
`A saída de "${owner.name}" não trouxe ${missing.map((path) => `\`${path}\``).join(', ')}, ` +
|
|
837
|
+
'declarado no assistente, como texto ou valor esperado; o recurso foi lido sem esses campos.',
|
|
838
|
+
{ code: 'runtime.assistant_path_missing', action: owner.name, resource, paths: missing },
|
|
839
|
+
)
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
ok: true,
|
|
843
|
+
resource: { kind: resource, id, title, label: assistant.label, skill: assistant.skill },
|
|
844
|
+
...(context !== undefined ? { context } : {}),
|
|
845
|
+
meta: result.meta,
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* As declarações de assistente registradas, com a action dona de cada uma (ADR 0053). Serve ao
|
|
851
|
+
* consumidor para montar rotas e telas a partir da declaração e para conferir as chaves de
|
|
852
|
+
* procedimento contra o registro que ele mantém.
|
|
853
|
+
*/
|
|
854
|
+
assistants(): RegisteredAssistant[] {
|
|
855
|
+
return [...this.assistantOwners.values()].map((owner) => ({
|
|
856
|
+
action: owner.name,
|
|
857
|
+
...publicAssistant(owner.assistant),
|
|
858
|
+
personalData: owner.personalData === true,
|
|
859
|
+
}))
|
|
860
|
+
}
|
|
861
|
+
|
|
729
862
|
/**
|
|
730
863
|
* Executa no worker um envelope criado por `execute()`. O consumer fornece
|
|
731
864
|
* auth/can reidratados; identidade, provenance e trace vêm do envelope.
|
|
@@ -1026,6 +1159,9 @@ export class Runtime {
|
|
|
1026
1159
|
metadata: {
|
|
1027
1160
|
...(effect !== undefined ? { effect } : {}),
|
|
1028
1161
|
...(action.personalData !== undefined ? { personalData: action.personalData } : {}),
|
|
1162
|
+
...(action.kind === 'view' && action.assistant !== undefined
|
|
1163
|
+
? { assistant: publicAssistant(action.assistant) }
|
|
1164
|
+
: {}),
|
|
1029
1165
|
dataProducts,
|
|
1030
1166
|
dataProductLabels: Object.fromEntries(
|
|
1031
1167
|
dataProducts.map((id) => [id, this.dataProducts.get(id)?.label ?? id]),
|
package/src/core/types.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* 13. RuntimeConfig (§14)
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import type { AssistantDeclaration } from './assistant.ts'
|
|
24
25
|
import type { SoftDeleteEntity } from './retention.ts'
|
|
25
26
|
|
|
26
27
|
// =============================================================================
|
|
@@ -548,6 +549,30 @@ export interface AIConfig {
|
|
|
548
549
|
requiresConfirmation?: boolean
|
|
549
550
|
}
|
|
550
551
|
|
|
552
|
+
/**
|
|
553
|
+
* O assistente do recurso que uma action `view` lê (ADR 0053). A action que o declara é a action
|
|
554
|
+
* dona do tipo de recurso: é por ela que o recurso é lido e que o acesso a ele é conferido. A
|
|
555
|
+
* declaração é serializável e não carrega função, consulta nem regra de acesso.
|
|
556
|
+
*/
|
|
557
|
+
export interface ActionAssistant {
|
|
558
|
+
/** O tipo de recurso de que a action é dona, em camelCase e único no runtime (`customer`). */
|
|
559
|
+
resource: string
|
|
560
|
+
/** A chave do procedimento que a IA segue ao tratar do recurso, em minúsculas com hífen. */
|
|
561
|
+
skill: string
|
|
562
|
+
/** O nome do assistente para pessoas (`Assistente do cliente`). */
|
|
563
|
+
label: string
|
|
564
|
+
/** O caminho, na saída, do campo que nomeia o recurso para pessoas (`name`). */
|
|
565
|
+
title: string
|
|
566
|
+
/**
|
|
567
|
+
* Os caminhos, na saída, dos campos que o host pode entregar ao modelo como dado do recurso. O
|
|
568
|
+
* ponto desce em objetos (`leads.totalCount`); um caminho que passa por uma lista se aplica a cada
|
|
569
|
+
* item.
|
|
570
|
+
*/
|
|
571
|
+
context: readonly string[]
|
|
572
|
+
/** Teto, em caracteres, do JSON de `context`. Padrão de 4.000, mínimo de 1.000, máximo de 16.000. */
|
|
573
|
+
maxContextChars?: number
|
|
574
|
+
}
|
|
575
|
+
|
|
551
576
|
export interface Example {
|
|
552
577
|
name: string
|
|
553
578
|
description?: string
|
|
@@ -943,6 +968,12 @@ export interface ViewAction<In, Out, ParsedIn = In> extends ActionBase<In, Out,
|
|
|
943
968
|
/** Entidades relacionadas projetáveis. View simples não projeta nada — omita. */
|
|
944
969
|
projection?: string[]
|
|
945
970
|
expand?: Record<string, ExpandSpec>
|
|
971
|
+
/**
|
|
972
|
+
* Torna esta action a dona de um tipo de recurso e declara o assistente dele (ADR 0053). Exige
|
|
973
|
+
* `ai` habilitado, `personalData` declarado e uma entrada em que `id`, do tipo texto, é o único
|
|
974
|
+
* campo obrigatório. O runtime lê o recurso por `runtime.readResource`.
|
|
975
|
+
*/
|
|
976
|
+
assistant?: ActionAssistant
|
|
946
977
|
}
|
|
947
978
|
|
|
948
979
|
/**
|
|
@@ -1341,6 +1372,9 @@ export interface AiTool {
|
|
|
1341
1372
|
effect?: ActionEffect
|
|
1342
1373
|
/** A action declara devolver dado pessoal; ausente quando não declarou. */
|
|
1343
1374
|
personalData?: boolean
|
|
1375
|
+
/** O assistente do recurso de que a action é dona (ADR 0053), com o teto resolvido; ausente
|
|
1376
|
+
* quando não declara. */
|
|
1377
|
+
assistant?: AssistantDeclaration
|
|
1344
1378
|
}
|
|
1345
1379
|
}
|
|
1346
1380
|
|
package/src/mcp/index.ts
CHANGED
|
@@ -58,6 +58,9 @@ export function createOpusMcpServer(runtime: Runtime, opts: OpusMcpOptions = {})
|
|
|
58
58
|
...(t.metadata?.personalData !== undefined
|
|
59
59
|
? { 'com.softize.opus/personal-data': t.metadata.personalData }
|
|
60
60
|
: {}),
|
|
61
|
+
// O assistente do recurso de que a action é dona (ADR 0053): o host conhece o assistente
|
|
62
|
+
// pelo catálogo que já consulta.
|
|
63
|
+
...(t.metadata?.assistant !== undefined ? { 'com.softize.opus/assistant': t.metadata.assistant } : {}),
|
|
61
64
|
},
|
|
62
65
|
})),
|
|
63
66
|
}))
|
|
@@ -136,7 +136,7 @@ name, kind
|
|
|
136
136
|
→ public, requires, authorize, loads
|
|
137
137
|
→ handler
|
|
138
138
|
→ audit, confirm, invalidates, rateLimit, idempotency
|
|
139
|
-
→ ai, automatable, internal
|
|
139
|
+
→ ai, assistant, automatable, internal
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
Campos específicos de um kind, como `fields`, `paginate`, `columns`, `filters`, `expand`,
|
|
@@ -179,6 +179,102 @@ A autorização é fechada por padrão: sem `public: true`, a action exige usuá
|
|
|
179
179
|
`authorize`, exige também a permissão declarada. `automatable: true` permite que um
|
|
180
180
|
[schedule](/docs/scheduler) execute a action como o sistema, sem abri-la a chamadores anônimos.
|
|
181
181
|
|
|
182
|
+
## Declarar o assistente do recurso
|
|
183
|
+
|
|
184
|
+
Um produto pode oferecer, além do chat geral, uma conversa sobre um recurso específico: um cliente,
|
|
185
|
+
um relatório, um pedido. Essa conversa precisa ler o recurso com a permissão de quem conversa, seguir
|
|
186
|
+
o procedimento próprio daquele tipo de recurso e entregar ao modelo só os dados necessários. Os três
|
|
187
|
+
ficam declarados num lugar só: a action `view` que lê o recurso declara `assistant` e passa a ser a
|
|
188
|
+
**action dona** daquele tipo de recurso.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
export const customerViewContract = defineContract({
|
|
192
|
+
name: 'customer.view',
|
|
193
|
+
kind: 'view',
|
|
194
|
+
label: 'Ver cliente',
|
|
195
|
+
input: z.object({ id: z.string().uuid() }),
|
|
196
|
+
output: customerViewSchema,
|
|
197
|
+
personalData: true,
|
|
198
|
+
requires: 'customer.read',
|
|
199
|
+
authorize: (ctx) => ctx.can('customer.read'),
|
|
200
|
+
ai: true,
|
|
201
|
+
assistant: {
|
|
202
|
+
resource: 'customer',
|
|
203
|
+
skill: 'review-customer',
|
|
204
|
+
label: 'Assistente do cliente',
|
|
205
|
+
title: 'name',
|
|
206
|
+
context: ['kind', 'stage', 'sources', 'leads.totalCount', 'leads.latest.status'],
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
| Campo | O que declara |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| `resource` | O tipo de recurso de que a action é dona, em camelCase. Cada tipo tem uma única action dona. |
|
|
214
|
+
| `skill` | A chave do procedimento que a IA segue ao tratar do recurso, em minúsculas com hífen. O Opus não conhece o procedimento; o produto o mantém e confere que a chave existe. |
|
|
215
|
+
| `label` | O nome do assistente para pessoas. Entra no inventário do `opus copy`. |
|
|
216
|
+
| `title` | O caminho, na saída, do campo que nomeia o recurso. |
|
|
217
|
+
| `context` | Os caminhos, na saída, dos campos que o host pode entregar ao modelo como dado do recurso. |
|
|
218
|
+
| `maxContextChars` | Opcional. O teto do dado entregue, em caracteres de JSON: 4.000 por padrão, de 1.000 a 16.000. |
|
|
219
|
+
|
|
220
|
+
A action dona precisa ser `view`, estar publicada para a IA (`ai`), declarar `personalData` e
|
|
221
|
+
receber só `{ id }`: o `id`, do tipo texto, é o único campo obrigatório da entrada, e outros campos,
|
|
222
|
+
se houver, têm valor padrão ou são opcionais. Os caminhos usam ponto para descer em objetos; um
|
|
223
|
+
caminho que passa por uma lista vale para cada item, e um campo intermediário `null` aparece como
|
|
224
|
+
`null`. Declare em `context` só o que orienta a conversa: o exemplo deixa fora contato, documento e
|
|
225
|
+
endereço, e o nome já vai como `title`. Um caminho que termina num objeto ou numa lista leva tudo o
|
|
226
|
+
que está abaixo dele, inclusive campos acrescentados depois à saída; para conferir num teste o que
|
|
227
|
+
cada caminho alcança, use `assistantContextLeaves`, de `@softize/opus/assistant-schema`, sobre o
|
|
228
|
+
schema de saída do manifest. O `title` precisa apontar para texto ou número.
|
|
229
|
+
|
|
230
|
+
O `opus check` cobra a forma da declaração (regra `assistant-declaration`), o `opus gen` recusa
|
|
231
|
+
`title`, caminho de `context` ou entrada que não conferem com os schemas, e o registro recusa duas
|
|
232
|
+
actions donas do mesmo recurso. O manifest, `runtime.aiTools()` e o MCP (`com.softize.opus/assistant`
|
|
233
|
+
em `_meta`) levam a declaração, e o manifest passa a mostrar também a publicação para a IA.
|
|
234
|
+
|
|
235
|
+
### Ler o recurso pela action dona
|
|
236
|
+
|
|
237
|
+
O recurso de um assistente é lido, e o acesso a ele é conferido, somente pela action dona.
|
|
238
|
+
`runtime.readResource` executa a action com `{ id }` pelo mesmo `execute` de qualquer chamada; a
|
|
239
|
+
autorização, a auditoria e a marca de dado pessoal são as da action:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const read = await runtime.readResource('customer', id, base, { context: true })
|
|
243
|
+
if (!read.ok) return notFound() // o erro da action, com a categoria dela
|
|
244
|
+
read.resource // { kind: 'customer', id, title: 'Maria Souza', label: 'Assistente do cliente', skill: 'review-customer' }
|
|
245
|
+
read.context // { data, truncated, omitted, personalData: true }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Sem `{ context: true }`, a leitura devolve só o descritor, que basta para conferir o acesso e
|
|
249
|
+
mostrar o nome. Um tipo sem action dona devolve `runtime.assistant_resource_unknown` sem executar
|
|
250
|
+
nada. `runtime.assistants()` lista as declarações com a action dona de cada uma, para o produto
|
|
251
|
+
montar rotas e telas sem lista paralela e para conferir, num teste, que cada `skill` existe no
|
|
252
|
+
registro de procedimentos dele. O Opus não enxerga as rotas HTTP que o produto registra por conta
|
|
253
|
+
própria: um teste do produto deve garantir que nenhuma rota confere acesso a recurso sem passar por
|
|
254
|
+
uma action.
|
|
255
|
+
|
|
256
|
+
### O dado que vai ao modelo
|
|
257
|
+
|
|
258
|
+
`context.data` traz só os caminhos declarados, com a estrutura da saída. Quando o JSON passa do
|
|
259
|
+
teto, o corte acontece por etapas, até caber, e fica registrado em `omitted`:
|
|
260
|
+
|
|
261
|
+
1. sai o último item da maior lista, repetidamente;
|
|
262
|
+
2. com as listas vazias, o texto mais longo é encurtado pela metade, até 200 caracteres;
|
|
263
|
+
3. por fim, saem campos inteiros, do último declarado para o primeiro.
|
|
264
|
+
|
|
265
|
+
Com qualquer corte, `truncated` vale `true`. O título fica fora do teto e é encurtado a 200
|
|
266
|
+
caracteres. Um caminho que falta na saída em execução é omitido com o motivo `missing` e gera um
|
|
267
|
+
aviso no log, sem falhar a leitura. Quem recebe a saída por outro canal, como o MCP, aplica a mesma
|
|
268
|
+
projeção com `projectAssistantContext(assistant, output)`.
|
|
269
|
+
|
|
270
|
+
Entregue o dado ao modelo como bloco delimitado, numa mensagem de dado, nunca no prompt de sistema.
|
|
271
|
+
`resourceDataBlock`, de `@softize/opus/ai`, monta o bloco com o tipo, o id, o título, os dados e,
|
|
272
|
+
quando houve corte ou ausência, `truncated` e `omitted`, e escapa dentro dos valores o que poderia
|
|
273
|
+
fechar o delimitador. Quando o modelo chama a action dona como ferramenta, recebe a saída completa
|
|
274
|
+
do contrato, como em qualquer chamada: a projeção governa só o que o host entrega por iniciativa
|
|
275
|
+
própria. Cada execução de uma action com dado pessoal é uma exposição registrada, então reaproveite
|
|
276
|
+
a leitura que já conferiu o acesso em vez de executar a action de novo só para obter o contexto.
|
|
277
|
+
|
|
182
278
|
## Prototipar com dados simulados
|
|
183
279
|
|
|
184
280
|
Para desenhar a interface antes de o backend existir, rode o app em modo design. Nesse modo, toda
|
|
@@ -86,8 +86,10 @@ const { text } = await ctx.ai!.run('Quais notas a Empresa X emitiu em junho?')
|
|
|
86
86
|
usado na execução e na auditoria; `title` traz a `label` legível da action, quando existe;
|
|
87
87
|
`description` vem de `ai.description` (ou da `description` da action) e `inputSchema`, do input.
|
|
88
88
|
`metadata.dataProducts` lista os [Produtos de Dados](/docs/data-products) relacionados e
|
|
89
|
-
`metadata.dataProductLabels` associa cada identificador ao seu nome legível.
|
|
90
|
-
|
|
89
|
+
`metadata.dataProductLabels` associa cada identificador ao seu nome legível. Na action dona de um
|
|
90
|
+
tipo de recurso, `metadata.assistant` traz o [assistente declarado](/docs/actions): o recurso, a
|
|
91
|
+
chave do procedimento, o nome, o campo do título, os campos do dado ao modelo e o teto. Esses
|
|
92
|
+
metadados servem ao host: não entram no prompt e não concedem acesso.
|
|
91
93
|
|
|
92
94
|
O agente só executa nomes que `aiTools()` anunciou; uma chamada a outra action volta como erro
|
|
93
95
|
para o modelo. Actions marcadas com `ai: { destructive: true }` ou `ai: { requiresConfirmation: true }`
|
|
@@ -21,7 +21,8 @@ opus seed check # bindings, dependências, ciclos e scripts paralelos de s
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
`opus check` lê o código-fonte sem executá-lo e aplica regras em quatro grupos: actions
|
|
24
|
-
(`action-name`, `kind`, `field-order`, `export`, `requires-sem-authorize`, `ignored-by-kind`
|
|
24
|
+
(`action-name`, `kind`, `field-order`, `export`, `requires-sem-authorize`, `ignored-by-kind` e
|
|
25
|
+
`assistant-declaration`, que cobra a forma do [assistente do recurso](/docs/actions)), UI
|
|
25
26
|
(`ui-structure`, `ui-structure-split`, `ui-semantic-api`, `removed-ui-token`, `unpaired-ui-surface`),
|
|
26
27
|
[Produtos de Dados](/docs/data-products) (`data-product-export`, `-id`, `-duplicate`, `-version`,
|
|
27
28
|
`-interfaces`, `-interface`, `-entities` e `-entity`, todas com o prefixo `data-product`) e
|
|
@@ -60,7 +60,9 @@ Quando a action declara o que faz com os dados, a tool traz também as anotaçõ
|
|
|
60
60
|
`readOnlyHint` vem de `effect`. Numa action que escreve, `destructiveHint` vai sempre explícito, a
|
|
61
61
|
partir de `ai.destructive`, porque o MCP trata a ausência numa escrita como destrutiva.
|
|
62
62
|
Em `_meta`, `com.softize.opus/effect` repete o efeito e `com.softize.opus/personal-data` diz se a
|
|
63
|
-
resposta contém dado pessoal.
|
|
63
|
+
resposta contém dado pessoal. Na action dona de um tipo de recurso, `com.softize.opus/assistant`
|
|
64
|
+
traz o assistente declarado, para o host conhecê-lo pelo catálogo que já consulta e aplicar à saída
|
|
65
|
+
a mesma projeção com `projectAssistantContext`. Veja [Actions](/docs/actions).
|
|
64
66
|
|
|
65
67
|
`CallTool` recusa com erro qualquer nome fora dessa lista; os demais seguem por `runtime.execute`.
|
|
66
68
|
A exposição declarada em `ai` decide o que o cliente MCP pode executar, e a autorização do contexto
|