agentex-creator-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +195 -0
  4. package/dist/packages/contracts/src/deployer-investigation.d.ts +1015 -0
  5. package/dist/packages/contracts/src/deployer-investigation.js +101 -0
  6. package/dist/packages/contracts/src/index.d.ts +1701 -0
  7. package/dist/packages/contracts/src/index.js +380 -0
  8. package/dist/packages/contracts/src/indexed-activity.d.ts +684 -0
  9. package/dist/packages/contracts/src/indexed-activity.js +71 -0
  10. package/dist/packages/contracts/src/indexed-agents.d.ts +299 -0
  11. package/dist/packages/contracts/src/indexed-agents.js +131 -0
  12. package/dist/packages/contracts/src/inspection.d.ts +906 -0
  13. package/dist/packages/contracts/src/inspection.js +114 -0
  14. package/dist/packages/contracts/src/kinds.d.ts +5398 -0
  15. package/dist/packages/contracts/src/kinds.js +156 -0
  16. package/dist/packages/contracts/src/report-presentation.d.ts +346 -0
  17. package/dist/packages/contracts/src/report-presentation.js +120 -0
  18. package/dist/packages/contracts/src/solana-inspection.d.ts +451 -0
  19. package/dist/packages/contracts/src/solana-inspection.js +94 -0
  20. package/dist/packages/contracts/src/token-market.d.ts +193 -0
  21. package/dist/packages/contracts/src/token-market.js +335 -0
  22. package/dist/packages/contracts/src/wallet-analysis.d.ts +866 -0
  23. package/dist/packages/contracts/src/wallet-analysis.js +89 -0
  24. package/dist/packages/contracts/src/watchtower.d.ts +1141 -0
  25. package/dist/packages/contracts/src/watchtower.js +196 -0
  26. package/dist/packages/contracts/src/workflow.d.ts +1568 -0
  27. package/dist/packages/contracts/src/workflow.js +651 -0
  28. package/dist/packages/inspector/src/decode.d.ts +23 -0
  29. package/dist/packages/inspector/src/decode.js +150 -0
  30. package/dist/packages/inspector/src/scope.d.ts +87 -0
  31. package/dist/packages/inspector/src/scope.js +64 -0
  32. package/dist/packages/model/src/analysis.d.ts +149 -0
  33. package/dist/packages/model/src/analysis.js +387 -0
  34. package/dist/packages/model/src/pricing.d.ts +38 -0
  35. package/dist/packages/model/src/pricing.js +49 -0
  36. package/dist/packages/model/src/retry.d.ts +20 -0
  37. package/dist/packages/model/src/retry.js +31 -0
  38. package/dist/packages/model/src/schema.d.ts +10 -0
  39. package/dist/packages/model/src/schema.js +51 -0
  40. package/dist/packages/model/src/summary.d.ts +91 -0
  41. package/dist/packages/model/src/summary.js +177 -0
  42. package/dist/packages/model/src/types.d.ts +81 -0
  43. package/dist/packages/model/src/types.js +19 -0
  44. package/dist/packages/monitoring/src/delivery.d.ts +32 -0
  45. package/dist/packages/monitoring/src/delivery.js +53 -0
  46. package/dist/packages/providers/src/chain-transport.d.ts +42 -0
  47. package/dist/packages/providers/src/chain-transport.js +57 -0
  48. package/dist/packages/providers/src/coverage.d.ts +105 -0
  49. package/dist/packages/providers/src/coverage.js +260 -0
  50. package/dist/packages/providers/src/health.d.ts +273 -0
  51. package/dist/packages/providers/src/health.js +505 -0
  52. package/dist/packages/providers/src/keyed.d.ts +96 -0
  53. package/dist/packages/providers/src/keyed.js +240 -0
  54. package/dist/packages/providers/src/snapshot.d.ts +61 -0
  55. package/dist/packages/providers/src/snapshot.js +77 -0
  56. package/dist/packages/publication/src/fixtures.d.ts +45 -0
  57. package/dist/packages/publication/src/fixtures.js +350 -0
  58. package/dist/packages/research/src/index.d.ts +188 -0
  59. package/dist/packages/research/src/index.js +829 -0
  60. package/dist/packages/runtime/src/checkpoints.d.ts +65 -0
  61. package/dist/packages/runtime/src/checkpoints.js +214 -0
  62. package/dist/packages/runtime/src/policy.d.ts +57 -0
  63. package/dist/packages/runtime/src/policy.js +296 -0
  64. package/dist/packages/sdk/src/bin/agentex-buyer.d.ts +2 -0
  65. package/dist/packages/sdk/src/bin/agentex-buyer.js +4 -0
  66. package/dist/packages/sdk/src/bin/agentex.d.ts +2 -0
  67. package/dist/packages/sdk/src/bin/agentex.js +3 -0
  68. package/dist/packages/sdk/src/buyer-cli.d.ts +10 -0
  69. package/dist/packages/sdk/src/buyer-cli.js +210 -0
  70. package/dist/packages/sdk/src/buyer.d.ts +534 -0
  71. package/dist/packages/sdk/src/buyer.js +441 -0
  72. package/dist/packages/sdk/src/cli.d.ts +14 -0
  73. package/dist/packages/sdk/src/cli.js +149 -0
  74. package/dist/packages/sdk/src/errors.d.ts +31 -0
  75. package/dist/packages/sdk/src/errors.js +24 -0
  76. package/dist/packages/sdk/src/index.d.ts +236 -0
  77. package/dist/packages/sdk/src/index.js +150 -0
  78. package/dist/packages/sdk/src/local.d.ts +11 -0
  79. package/dist/packages/sdk/src/local.js +110 -0
  80. package/dist/packages/sdk/src/rails.d.ts +59 -0
  81. package/dist/packages/sdk/src/rails.js +96 -0
  82. package/dist/packages/sdk/src/report.d.ts +121 -0
  83. package/dist/packages/sdk/src/report.js +114 -0
  84. package/dist/packages/sdk/src/version.d.ts +2 -0
  85. package/dist/packages/sdk/src/version.js +2 -0
  86. package/dist/packages/watchtower/src/index.d.ts +150 -0
  87. package/dist/packages/watchtower/src/index.js +786 -0
  88. package/dist/packages/workflow/src/registry.d.ts +61 -0
  89. package/dist/packages/workflow/src/registry.js +76 -0
  90. package/examples/README.md +34 -0
  91. package/examples/cli-usage.sh +30 -0
  92. package/examples/fixtures/base-weth-input.json +4 -0
  93. package/examples/focused-researcher.json +127 -0
  94. package/examples/pay-with-eth-robinhood.mts +37 -0
  95. package/examples/pay-with-usdc.mts +41 -0
  96. package/examples/quickstart.mts +73 -0
  97. package/package.json +50 -0
@@ -0,0 +1,156 @@
1
+ import { z } from 'zod';
2
+ import { AtomicAmountSchema, contentHash, ResearchInputSchema, SupportedTokenResearcherManifestSchema, TOKEN_RESEARCHER_MANIFEST, TOKEN_RESEARCHER_MANIFEST_V2, TokenResearchReportSchema } from './index.js';
3
+ import { SupportedTransactionInspectorManifestSchema, TRANSACTION_INSPECTOR_MANIFEST, TRANSACTION_INSPECTOR_MANIFEST_V2, TransactionInspectionInputSchema, TransactionInspectionReportSchema } from './inspection.js';
4
+ import { SOLANA_INSPECTION_RPC_CEILING, SOLANA_INSPECTION_SCHEMA_ID, SOLANA_INSPECTION_TOOL, SolanaTransactionInspectionInputSchema, SolanaTransactionInspectionReportSchema } from './solana-inspection.js';
5
+ import { SupportedWatchtowerManifestSchema, WATCHTOWER_MANIFEST, WATCHTOWER_MANIFEST_V2, WATCHTOWER_MANIFEST_V3, WatchtowerWindowInputSchema, WatchtowerWindowReportSchema } from './watchtower.js';
6
+ import { WORKFLOW_SCHEMA_ID, WorkflowManifestSchema, WorkflowReportSchema, WorkflowRunInputSchema } from './workflow.js';
7
+ import { DEPLOYER_INVESTIGATOR_MANIFEST, DEPLOYER_SCHEMA_ID, DeployerInvestigationInputSchema, DeployerInvestigationReportSchema, SupportedDeployerInvestigatorManifestSchema } from './deployer-investigation.js';
8
+ import { SupportedWalletAnalystManifestSchema, WALLET_ANALYST_MANIFEST, WALLET_SCHEMA_ID, WalletAnalysisInputSchema, WalletAnalysisReportSchema } from './wallet-analysis.js';
9
+ /**
10
+ * Solana Transaction Inspector as an agent kind (P05). The executable contract (input, report, RPC ceiling) is owned by
11
+ * solana-inspection.ts; this file adds only the immutable manifest, its supported subset and the buyer result contract.
12
+ * The manifest uses its own strict schema (like workflows), so `AgentManifestSchema` and every existing manifest hash stay unchanged.
13
+ */
14
+ const Identifier = z.string().regex(/^[a-z][a-z0-9._-]{0,63}$/);
15
+ const Semver = z.string().regex(/^\d+\.\d+\.\d+$/);
16
+ const SOLANA_INSPECTOR_TEMPLATE_SOURCE = {
17
+ schemaVersion: '1.0', id: 'solana-transaction-inspector', version: '0.1.0', name: 'Solana Transaction Inspector',
18
+ description: 'Decode a proposed Solana legacy or v0 payload offline, resolve address lookup tables at a confirmed lower-bound slot and run simulateTransaction with signature verification disabled. It never signs or broadcasts, computes no asset flows and gives no safety verdict.',
19
+ author: { id: 'agentex', name: 'AGENTEX', ownership: 'first-party' }, license: 'Proprietary', visibility: 'source-visible', chains: ['solana'],
20
+ inputSchema: { type: 'object', additionalProperties: false, required: ['analysisChain', 'network', 'encoding', 'payloadBase64'], properties: {
21
+ analysisChain: { type: 'string', description: 'Explicit analysis chain; independent of the EVM sign-in network', enum: ['solana'] },
22
+ network: { type: 'string', description: 'Solana cluster', enum: ['mainnet-beta', 'devnet'] }, encoding: { type: 'string', description: 'Serialized transaction or message', enum: ['transaction', 'message'] },
23
+ payloadBase64: { type: 'string', description: 'Canonical base64 payload, at most 1232 bytes' }, simulate: { type: 'boolean', description: 'Run simulateTransaction (default true)' },
24
+ accounts: { type: 'array', description: 'Up to eight accounts whose post-simulation state is reported' }
25
+ } },
26
+ outputSchemaId: SOLANA_INSPECTION_SCHEMA_ID,
27
+ tools: [{ id: 'solana.inspect-transaction', version: '0.1.0', capability: 'solana.read', chains: ['solana'], addressScope: 'input-transaction', maximumCalls: SOLANA_INSPECTION_RPC_CEILING }],
28
+ modelPolicy: { mode: 'deterministic', maximumModelTokens: 0 },
29
+ limits: { maximumRpcCalls: SOLANA_INSPECTION_RPC_CEILING, maximumDurationMs: 60000, maximumOutputBytes: 262144, maximumGraphDepth: 3, maximumPages: 0, maximumExecutionAtomic: '0' },
30
+ workflow: [{ id: 'input', type: 'input', dependsOn: [] }, { id: 'inspect', type: 'tool', tool: 'solana.inspect-transaction', dependsOn: ['input'], arguments: { payload: { source: 'input', path: 'payloadBase64' } } }, { id: 'report', type: 'output', dependsOn: ['inspect'], from: 'inspect' }],
31
+ testCases: ['legacy-system-spl-token', 'v0-lookup-table', 'unknown-program', 'simulation-failure', 'budget-stop', 'wrong-genesis'],
32
+ };
33
+ export const SolanaTransactionInspectorManifestSchema = z.strictObject({
34
+ schemaVersion: z.literal('1.0'), id: Identifier, version: Semver, name: z.string().min(1).max(100), description: z.string().min(1).max(2000),
35
+ author: z.strictObject({ id: Identifier, name: z.string().min(1).max(100), ownership: z.enum(['first-party', 'independent']) }),
36
+ license: z.string().min(1).max(100), visibility: z.enum(['private', 'source-visible', 'source-private']), chains: z.tuple([z.literal('solana')]),
37
+ inputSchema: z.json(), outputSchemaId: z.literal(SOLANA_INSPECTION_SCHEMA_ID),
38
+ tools: z.tuple([z.strictObject({ id: z.literal('solana.inspect-transaction'), version: z.literal('0.1.0'), capability: z.literal('solana.read'), chains: z.tuple([z.literal('solana')]), addressScope: z.literal('input-transaction'), maximumCalls: z.number().int().min(1).max(SOLANA_INSPECTION_RPC_CEILING) })]),
39
+ modelPolicy: z.strictObject({ mode: z.literal('deterministic'), maximumModelTokens: z.literal(0) }),
40
+ limits: z.strictObject({ maximumRpcCalls: z.number().int().min(1).max(SOLANA_INSPECTION_RPC_CEILING), maximumDurationMs: z.number().int().min(1000).max(60000), maximumOutputBytes: z.number().int().min(1024).max(262144), maximumGraphDepth: z.number().int().min(3).max(4), maximumPages: z.literal(0), maximumExecutionAtomic: AtomicAmountSchema }),
41
+ workflow: z.json(), testCases: z.array(Identifier).min(1).max(100),
42
+ }).superRefine((manifest, ctx) => {
43
+ if (contentHash(manifest.workflow) !== contentHash(SOLANA_INSPECTOR_TEMPLATE_SOURCE.workflow))
44
+ ctx.addIssue({ code: 'custom', path: ['workflow'], message: 'The executor supports only the bounded Solana Transaction Inspector graph.' });
45
+ if (contentHash(manifest.inputSchema) !== contentHash(SOLANA_INSPECTOR_TEMPLATE_SOURCE.inputSchema))
46
+ ctx.addIssue({ code: 'custom', path: ['inputSchema'], message: 'The input declaration must match the supported Solana payload inputs.' });
47
+ if (manifest.tools[0].maximumCalls > manifest.limits.maximumRpcCalls)
48
+ ctx.addIssue({ code: 'custom', path: ['tools', 0, 'maximumCalls'], message: 'The tool ceiling exceeds the run RPC budget.' });
49
+ });
50
+ const deepFreeze = (value) => { if (value !== null && typeof value === 'object') {
51
+ for (const child of Object.values(value))
52
+ deepFreeze(child);
53
+ Object.freeze(value);
54
+ } return value; };
55
+ export const SOLANA_TRANSACTION_INSPECTOR_MANIFEST = deepFreeze(SolanaTransactionInspectorManifestSchema.parse(structuredClone(SOLANA_INSPECTOR_TEMPLATE_SOURCE)));
56
+ /** Buyer result contract for Solana inspections. Asset flows are never claimed; simulation is a snapshot at its context slot. */
57
+ export const SOLANA_INSPECTION_RESULT_CONTRACT = { id: 'solana-inspection-observed-v1', description: 'Charge only for a complete inspection: the payload decoded with no invalid instruction, the cluster genesis hash was captured, and a simulateTransaction outcome (succeeded or failed) was observed with its captured receipt at a confirmed lower-bound slot. Nothing is signed or broadcast; asset flows are not computed.' };
58
+ export function solanaInspectionResultContractIssues(raw) {
59
+ const parsed = SolanaTransactionInspectionReportSchema.safeParse(raw);
60
+ if (!parsed.success)
61
+ return ['The report does not match agentex.solana-transaction-inspection.v1.'];
62
+ const report = parsed.data;
63
+ const issues = [];
64
+ const evidence = new Map(report.evidence.map((item) => [item.id, item]));
65
+ if (report.status !== 'complete')
66
+ issues.push(`The inspection is ${report.status}.`);
67
+ if (report.payload.version === null || report.instructions.some((item) => item.status === 'invalid'))
68
+ issues.push('The payload was not fully decoded.');
69
+ if (!report.genesisHash || !report.evidence.some((item) => item.source.method === 'getGenesisHash' && item.completeness === 'complete-for-request'))
70
+ issues.push('The cluster identity was not captured.');
71
+ if (report.finalizedSlotLowerBound === null)
72
+ issues.push('No confirmed lower-bound slot was captured.');
73
+ if (!['succeeded', 'failed'].includes(report.simulation.status) || !report.simulation.evidenceIds.length
74
+ || report.simulation.evidenceIds.some((id) => evidence.get(id)?.source.method !== 'simulateTransaction' || evidence.get(id)?.completeness !== 'complete-for-request'))
75
+ issues.push('No simulation outcome is backed by a captured simulateTransaction receipt.');
76
+ if (report.evidence.some((item) => item.tool !== SOLANA_INSPECTION_TOOL))
77
+ issues.push('Evidence came from a tool other than the declared inspector.');
78
+ return issues;
79
+ }
80
+ export const satisfiesSolanaInspectionResultContract = (raw) => solanaInspectionResultContractIssues(raw).length === 0;
81
+ /**
82
+ * Executable agent kinds. A manifest's `outputSchemaId` is its explicit kind; there is no inference from names or graphs.
83
+ * Services that support only some kinds must call `requireAgentKind` before any work, quote, listing or review.
84
+ * `workflow` is the general creator workflow (`agentex.workflow.v1`); its manifests use `WorkflowManifestSchema`, not `AgentManifestSchema`.
85
+ * `solana-transaction-inspection` uses `SolanaTransactionInspectorManifestSchema`.
86
+ */
87
+ export const AgentKindSchema = z.enum(['token-research', 'transaction-inspection', 'watchtower', 'workflow', 'solana-transaction-inspection', 'deployer-investigation', 'wallet-analysis']);
88
+ export const AGENT_KINDS = {
89
+ 'token-research': { outputSchemaId: 'agentex.token-research.v1', template: TOKEN_RESEARCHER_MANIFEST_V2, supportedManifest: SupportedTokenResearcherManifestSchema, input: ResearchInputSchema, report: TokenResearchReportSchema },
90
+ 'transaction-inspection': { outputSchemaId: 'agentex.transaction-inspection.v1', template: TRANSACTION_INSPECTOR_MANIFEST_V2, supportedManifest: SupportedTransactionInspectorManifestSchema, input: TransactionInspectionInputSchema, report: TransactionInspectionReportSchema },
91
+ watchtower: { outputSchemaId: 'agentex.watchtower-window.v1', template: WATCHTOWER_MANIFEST_V3, supportedManifest: SupportedWatchtowerManifestSchema, input: WatchtowerWindowInputSchema, report: WatchtowerWindowReportSchema },
92
+ /** Workflow templates depend on the tool registry and live in packages/workflow. */
93
+ workflow: { outputSchemaId: WORKFLOW_SCHEMA_ID, template: null, supportedManifest: WorkflowManifestSchema, input: WorkflowRunInputSchema, report: WorkflowReportSchema },
94
+ 'solana-transaction-inspection': { outputSchemaId: SOLANA_INSPECTION_SCHEMA_ID, template: SOLANA_TRANSACTION_INSPECTOR_MANIFEST, supportedManifest: SolanaTransactionInspectorManifestSchema, input: SolanaTransactionInspectionInputSchema, report: SolanaTransactionInspectionReportSchema },
95
+ /** P02/P03 indexed first-party agents: strict manifests of their own schema (packages/contracts deployer-investigation.ts, wallet-analysis.ts). */
96
+ 'deployer-investigation': { outputSchemaId: DEPLOYER_SCHEMA_ID, template: DEPLOYER_INVESTIGATOR_MANIFEST, supportedManifest: SupportedDeployerInvestigatorManifestSchema, input: DeployerInvestigationInputSchema, report: DeployerInvestigationReportSchema },
97
+ 'wallet-analysis': { outputSchemaId: WALLET_SCHEMA_ID, template: WALLET_ANALYST_MANIFEST, supportedManifest: SupportedWalletAnalystManifestSchema, input: WalletAnalysisInputSchema, report: WalletAnalysisReportSchema },
98
+ };
99
+ /** Indexed-agent kinds whose manifests use their own strict schema, tool evidence envelopes and the one-request AI-assisted summary. */
100
+ export const INDEXED_AGENT_KINDS = Object.freeze(['deployer-investigation', 'wallet-analysis']);
101
+ export const isIndexedAgentKind = (kind) => kind === 'deployer-investigation' || kind === 'wallet-analysis';
102
+ /** Indexed first-party templates (0.1.0), offered for new drafts beside `AGENT_TEMPLATES` (which keeps only `AgentManifestSchema` manifests). */
103
+ export const INDEXED_AGENT_TEMPLATES = Object.freeze([DEPLOYER_INVESTIGATOR_MANIFEST, WALLET_ANALYST_MANIFEST]);
104
+ /**
105
+ * Current first-party EVM templates offered for new drafts (0.2.0, instant-read wording). The 0.1.0 manifests stay exported and byte-identical;
106
+ * versions created from them keep their stored text and still validate. The Solana template is `AGENT_KINDS['solana-transaction-inspection'].template`.
107
+ */
108
+ export const AGENT_TEMPLATES = Object.freeze([TOKEN_RESEARCHER_MANIFEST_V2, TRANSACTION_INSPECTOR_MANIFEST_V2, WATCHTOWER_MANIFEST_V3]);
109
+ /** Every first-party EVM template version, oldest first. */
110
+ export const AGENT_TEMPLATE_VERSIONS = Object.freeze([TOKEN_RESEARCHER_MANIFEST, TOKEN_RESEARCHER_MANIFEST_V2, TRANSACTION_INSPECTOR_MANIFEST, TRANSACTION_INSPECTOR_MANIFEST_V2, WATCHTOWER_MANIFEST, WATCHTOWER_MANIFEST_V2, WATCHTOWER_MANIFEST_V3]);
111
+ export function agentKindOf(manifest) {
112
+ const id = manifest?.outputSchemaId;
113
+ for (const kind of AgentKindSchema.options)
114
+ if (AGENT_KINDS[kind].outputSchemaId === id)
115
+ return kind;
116
+ return null;
117
+ }
118
+ export class UnsupportedAgentKindError extends Error {
119
+ kind;
120
+ code = 'UNSUPPORTED_AGENT_KIND';
121
+ constructor(kind, context) {
122
+ super(`${context} does not support ${kind ?? 'this unknown'} agent versions.`);
123
+ this.kind = kind;
124
+ this.name = 'UnsupportedAgentKindError';
125
+ }
126
+ }
127
+ export function requireAgentKind(manifest, allowed, context) {
128
+ const kind = agentKindOf(manifest);
129
+ if (!kind || !allowed.includes(kind))
130
+ throw new UnsupportedAgentKindError(kind, context);
131
+ return kind;
132
+ }
133
+ /** Union for typing and generic clients. Use `parseRunInput` for precise per-kind validation messages. */
134
+ export const RunInputSchema = z.union([ResearchInputSchema, TransactionInspectionInputSchema, WatchtowerWindowInputSchema, WorkflowRunInputSchema, SolanaTransactionInspectionInputSchema, DeployerInvestigationInputSchema, WalletAnalysisInputSchema]);
135
+ /** Workflow inputs carry `workflow`, Solana inspections `analysisChain`, deployer investigations `contract`, wallet analyses `wallet`, token research `address`, Watchtower windows `rules`, EVM payload inspections `from`. */
136
+ export const runInputKind = (input) => 'workflow' in input ? 'workflow' : 'analysisChain' in input ? 'solana-transaction-inspection' : 'wallet' in input ? 'wallet-analysis' : 'contract' in input ? 'deployer-investigation'
137
+ : 'address' in input ? 'token-research' : 'rules' in input ? 'watchtower' : 'transaction-inspection';
138
+ /** The manifest chain an input targets (`solana` for either Solana cluster). */
139
+ export const runAnalysisChain = (input) => 'analysisChain' in input ? input.analysisChain : input.chain;
140
+ /** The E05 coverage chain: Solana devnet is a separate coverage entry. */
141
+ export const runCoverageChain = (input) => 'analysisChain' in input ? (input.network === 'mainnet-beta' ? 'solana' : 'solana-devnet') : input.chain;
142
+ export function parseRunInput(raw, kind) {
143
+ const object = raw !== null && typeof raw === 'object' && !Array.isArray(raw);
144
+ const selected = kind ?? (object && Object.hasOwn(raw, 'workflow') ? 'workflow' : object && Object.hasOwn(raw, 'analysisChain') ? 'solana-transaction-inspection'
145
+ : object && Object.hasOwn(raw, 'wallet') && !Object.hasOwn(raw, 'address') ? 'wallet-analysis' : object && Object.hasOwn(raw, 'contract') && !Object.hasOwn(raw, 'address') && !Object.hasOwn(raw, 'rules') ? 'deployer-investigation'
146
+ : object && !Object.hasOwn(raw, 'address') && Object.hasOwn(raw, 'rules') ? 'watchtower'
147
+ : object && !Object.hasOwn(raw, 'address') && (Object.hasOwn(raw, 'from') || Object.hasOwn(raw, 'data')) ? 'transaction-inspection' : 'token-research');
148
+ return selected === 'workflow' ? WorkflowRunInputSchema.parse(raw) : selected === 'solana-transaction-inspection' ? SolanaTransactionInspectionInputSchema.parse(raw) : selected === 'token-research' ? ResearchInputSchema.parse(raw)
149
+ : selected === 'deployer-investigation' ? DeployerInvestigationInputSchema.parse(raw) : selected === 'wallet-analysis' ? WalletAnalysisInputSchema.parse(raw)
150
+ : selected === 'watchtower' ? WatchtowerWindowInputSchema.parse(raw) : TransactionInspectionInputSchema.parse(raw);
151
+ }
152
+ export function parseRunOutput(kind, raw) {
153
+ return kind === 'workflow' ? WorkflowReportSchema.parse(raw) : kind === 'solana-transaction-inspection' ? SolanaTransactionInspectionReportSchema.parse(raw) : kind === 'token-research' ? TokenResearchReportSchema.parse(raw)
154
+ : kind === 'deployer-investigation' ? DeployerInvestigationReportSchema.parse(raw) : kind === 'wallet-analysis' ? WalletAnalysisReportSchema.parse(raw)
155
+ : kind === 'watchtower' ? WatchtowerWindowReportSchema.parse(raw) : TransactionInspectionReportSchema.parse(raw);
156
+ }
@@ -0,0 +1,346 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Shared report presentation (R01). Every AGENTEX report renders in one shell (Overview, Findings, Evidence, Sources, Execution, Export);
4
+ * the archetype and blocks choose how its content is laid out. A workflow version declares them in its manifest: which tool outputs become
5
+ * bounded tables (`datasets`), which tool observations are carried into the report (`signals`), which log windows state coverage, and the
6
+ * ordered blocks that render them. Presentation is layout only: it never grants a capability, adds a read or changes a finding, and every
7
+ * row it carries keeps the evidence of the step that produced it. Fixed first-party kinds map their reports to the same blocks in the web.
8
+ */
9
+ export declare const REPORT_ARCHETYPES: readonly ["token-protocol", "wallet-flow", "contract-security", "market-liquidity", "governance-treasury", "monitoring-events"];
10
+ export declare const ReportArchetypeSchema: z.ZodEnum<{
11
+ "token-protocol": "token-protocol";
12
+ "wallet-flow": "wallet-flow";
13
+ "contract-security": "contract-security";
14
+ "market-liquidity": "market-liquidity";
15
+ "governance-treasury": "governance-treasury";
16
+ "monitoring-events": "monitoring-events";
17
+ }>;
18
+ export type ReportArchetype = z.infer<typeof ReportArchetypeSchema>;
19
+ /** Column types decide formatting only (addresses shorten and link, amounts keep their exact text); values are never converted. */
20
+ export declare const DatasetColumnTypeSchema: z.ZodEnum<{
21
+ boolean: "boolean";
22
+ address: "address";
23
+ integer: "integer";
24
+ label: "label";
25
+ text: "text";
26
+ amount: "amount";
27
+ percent: "percent";
28
+ block: "block";
29
+ transaction: "transaction";
30
+ time: "time";
31
+ }>;
32
+ export type DatasetColumnType = z.infer<typeof DatasetColumnTypeSchema>;
33
+ export declare const DatasetSpecSchema: z.ZodObject<{
34
+ id: z.ZodString;
35
+ title: z.ZodString;
36
+ node: z.ZodString;
37
+ path: z.ZodString;
38
+ columns: z.ZodArray<z.ZodObject<{
39
+ key: z.ZodString;
40
+ label: z.ZodString;
41
+ type: z.ZodEnum<{
42
+ boolean: "boolean";
43
+ address: "address";
44
+ integer: "integer";
45
+ label: "label";
46
+ text: "text";
47
+ amount: "amount";
48
+ percent: "percent";
49
+ block: "block";
50
+ transaction: "transaction";
51
+ time: "time";
52
+ }>;
53
+ path: z.ZodString;
54
+ }, z.core.$strict>>;
55
+ maxRows: z.ZodNumber;
56
+ }, z.core.$strict>;
57
+ export type DatasetSpec = z.infer<typeof DatasetSpecSchema>;
58
+ export declare const ReportBlockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
59
+ type: z.ZodLiteral<"metrics">;
60
+ title: z.ZodString;
61
+ items: z.ZodArray<z.ZodObject<{
62
+ section: z.ZodString;
63
+ finding: z.ZodString;
64
+ }, z.core.$strict>>;
65
+ }, z.core.$strict>, z.ZodObject<{
66
+ type: z.ZodLiteral<"section">;
67
+ section: z.ZodString;
68
+ }, z.core.$strict>, z.ZodObject<{
69
+ type: z.ZodLiteral<"table">;
70
+ dataset: z.ZodString;
71
+ title: z.ZodOptional<z.ZodString>;
72
+ }, z.core.$strict>, z.ZodObject<{
73
+ type: z.ZodLiteral<"bars">;
74
+ dataset: z.ZodString;
75
+ label: z.ZodString;
76
+ value: z.ZodString;
77
+ title: z.ZodOptional<z.ZodString>;
78
+ }, z.core.$strict>, z.ZodObject<{
79
+ type: z.ZodLiteral<"timeline">;
80
+ dataset: z.ZodString;
81
+ at: z.ZodString;
82
+ label: z.ZodString;
83
+ detail: z.ZodOptional<z.ZodString>;
84
+ title: z.ZodOptional<z.ZodString>;
85
+ }, z.core.$strict>, z.ZodObject<{
86
+ type: z.ZodLiteral<"flow">;
87
+ dataset: z.ZodString;
88
+ from: z.ZodString;
89
+ to: z.ZodString;
90
+ amount: z.ZodOptional<z.ZodString>;
91
+ title: z.ZodOptional<z.ZodString>;
92
+ }, z.core.$strict>, z.ZodObject<{
93
+ type: z.ZodLiteral<"graph">;
94
+ dataset: z.ZodString;
95
+ to: z.ZodString;
96
+ relation: z.ZodOptional<z.ZodString>;
97
+ from: z.ZodOptional<z.ZodString>;
98
+ title: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strict>, z.ZodObject<{
100
+ type: z.ZodLiteral<"signals">;
101
+ title: z.ZodOptional<z.ZodString>;
102
+ }, z.core.$strict>, z.ZodObject<{
103
+ type: z.ZodLiteral<"coverage">;
104
+ title: z.ZodOptional<z.ZodString>;
105
+ }, z.core.$strict>, z.ZodObject<{
106
+ type: z.ZodLiteral<"limitations">;
107
+ title: z.ZodOptional<z.ZodString>;
108
+ }, z.core.$strict>], "type">;
109
+ export type ReportBlock = z.infer<typeof ReportBlockSchema>;
110
+ export declare const WorkflowPresentationSchema: z.ZodObject<{
111
+ archetype: z.ZodEnum<{
112
+ "token-protocol": "token-protocol";
113
+ "wallet-flow": "wallet-flow";
114
+ "contract-security": "contract-security";
115
+ "market-liquidity": "market-liquidity";
116
+ "governance-treasury": "governance-treasury";
117
+ "monitoring-events": "monitoring-events";
118
+ }>;
119
+ headline: z.ZodOptional<z.ZodObject<{
120
+ section: z.ZodString;
121
+ finding: z.ZodString;
122
+ }, z.core.$strict>>;
123
+ datasets: z.ZodOptional<z.ZodArray<z.ZodObject<{
124
+ id: z.ZodString;
125
+ title: z.ZodString;
126
+ node: z.ZodString;
127
+ path: z.ZodString;
128
+ columns: z.ZodArray<z.ZodObject<{
129
+ key: z.ZodString;
130
+ label: z.ZodString;
131
+ type: z.ZodEnum<{
132
+ boolean: "boolean";
133
+ address: "address";
134
+ integer: "integer";
135
+ label: "label";
136
+ text: "text";
137
+ amount: "amount";
138
+ percent: "percent";
139
+ block: "block";
140
+ transaction: "transaction";
141
+ time: "time";
142
+ }>;
143
+ path: z.ZodString;
144
+ }, z.core.$strict>>;
145
+ maxRows: z.ZodNumber;
146
+ }, z.core.$strict>>>;
147
+ signals: z.ZodOptional<z.ZodArray<z.ZodString>>;
148
+ coverage: z.ZodOptional<z.ZodArray<z.ZodObject<{
149
+ node: z.ZodString;
150
+ path: z.ZodString;
151
+ label: z.ZodString;
152
+ }, z.core.$strict>>>;
153
+ blocks: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
154
+ type: z.ZodLiteral<"metrics">;
155
+ title: z.ZodString;
156
+ items: z.ZodArray<z.ZodObject<{
157
+ section: z.ZodString;
158
+ finding: z.ZodString;
159
+ }, z.core.$strict>>;
160
+ }, z.core.$strict>, z.ZodObject<{
161
+ type: z.ZodLiteral<"section">;
162
+ section: z.ZodString;
163
+ }, z.core.$strict>, z.ZodObject<{
164
+ type: z.ZodLiteral<"table">;
165
+ dataset: z.ZodString;
166
+ title: z.ZodOptional<z.ZodString>;
167
+ }, z.core.$strict>, z.ZodObject<{
168
+ type: z.ZodLiteral<"bars">;
169
+ dataset: z.ZodString;
170
+ label: z.ZodString;
171
+ value: z.ZodString;
172
+ title: z.ZodOptional<z.ZodString>;
173
+ }, z.core.$strict>, z.ZodObject<{
174
+ type: z.ZodLiteral<"timeline">;
175
+ dataset: z.ZodString;
176
+ at: z.ZodString;
177
+ label: z.ZodString;
178
+ detail: z.ZodOptional<z.ZodString>;
179
+ title: z.ZodOptional<z.ZodString>;
180
+ }, z.core.$strict>, z.ZodObject<{
181
+ type: z.ZodLiteral<"flow">;
182
+ dataset: z.ZodString;
183
+ from: z.ZodString;
184
+ to: z.ZodString;
185
+ amount: z.ZodOptional<z.ZodString>;
186
+ title: z.ZodOptional<z.ZodString>;
187
+ }, z.core.$strict>, z.ZodObject<{
188
+ type: z.ZodLiteral<"graph">;
189
+ dataset: z.ZodString;
190
+ to: z.ZodString;
191
+ relation: z.ZodOptional<z.ZodString>;
192
+ from: z.ZodOptional<z.ZodString>;
193
+ title: z.ZodOptional<z.ZodString>;
194
+ }, z.core.$strict>, z.ZodObject<{
195
+ type: z.ZodLiteral<"signals">;
196
+ title: z.ZodOptional<z.ZodString>;
197
+ }, z.core.$strict>, z.ZodObject<{
198
+ type: z.ZodLiteral<"coverage">;
199
+ title: z.ZodOptional<z.ZodString>;
200
+ }, z.core.$strict>, z.ZodObject<{
201
+ type: z.ZodLiteral<"limitations">;
202
+ title: z.ZodOptional<z.ZodString>;
203
+ }, z.core.$strict>], "type">>;
204
+ }, z.core.$strict>;
205
+ export type WorkflowPresentation = z.infer<typeof WorkflowPresentationSchema>;
206
+ /** Reference checks against the manifest: nodes are tool nodes, datasets and columns exist, findings are declared. */
207
+ export declare function presentationIssues(presentation: WorkflowPresentation, manifest: {
208
+ workflow: readonly {
209
+ id: string;
210
+ type: string;
211
+ }[];
212
+ outputSchema: {
213
+ sections: readonly {
214
+ id: string;
215
+ findings: readonly {
216
+ code: string;
217
+ }[];
218
+ }[];
219
+ };
220
+ }): {
221
+ path: (string | number)[];
222
+ message: string;
223
+ }[];
224
+ export declare const ReportDatasetSchema: z.ZodObject<{
225
+ id: z.ZodString;
226
+ title: z.ZodString;
227
+ node: z.ZodString;
228
+ status: z.ZodEnum<{
229
+ observed: "observed";
230
+ unavailable: "unavailable";
231
+ absent: "absent";
232
+ }>;
233
+ reason: z.ZodNullable<z.ZodString>;
234
+ columns: z.ZodArray<z.ZodObject<{
235
+ key: z.ZodString;
236
+ label: z.ZodString;
237
+ type: z.ZodEnum<{
238
+ boolean: "boolean";
239
+ address: "address";
240
+ integer: "integer";
241
+ label: "label";
242
+ text: "text";
243
+ amount: "amount";
244
+ percent: "percent";
245
+ block: "block";
246
+ transaction: "transaction";
247
+ time: "time";
248
+ }>;
249
+ }, z.core.$strict>>;
250
+ rows: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
251
+ totalRows: z.ZodNumber;
252
+ truncated: z.ZodBoolean;
253
+ evidenceIds: z.ZodArray<z.ZodString>;
254
+ }, z.core.$strict>;
255
+ export type ReportDataset = z.infer<typeof ReportDatasetSchema>;
256
+ export declare const ReportSignalSchema: z.ZodObject<{
257
+ node: z.ZodString;
258
+ code: z.ZodString;
259
+ message: z.ZodString;
260
+ evidenceIds: z.ZodArray<z.ZodString>;
261
+ }, z.core.$strict>;
262
+ export type ReportSignal = z.infer<typeof ReportSignalSchema>;
263
+ export declare const ReportCoverageSchema: z.ZodObject<{
264
+ node: z.ZodString;
265
+ label: z.ZodString;
266
+ status: z.ZodEnum<{
267
+ observed: "observed";
268
+ unavailable: "unavailable";
269
+ absent: "absent";
270
+ }>;
271
+ fromBlock: z.ZodNullable<z.ZodString>;
272
+ toBlock: z.ZodNullable<z.ZodString>;
273
+ blocks: z.ZodNullable<z.ZodNumber>;
274
+ requestedBlocks: z.ZodNullable<z.ZodNumber>;
275
+ complete: z.ZodNullable<z.ZodBoolean>;
276
+ reason: z.ZodNullable<z.ZodString>;
277
+ approximateDuration: z.ZodNullable<z.ZodString>;
278
+ }, z.core.$strict>;
279
+ export type ReportCoverage = z.infer<typeof ReportCoverageSchema>;
280
+ /** `agentName` is the listed version's name, so shared and saved reports can title themselves without the manifest. */
281
+ export declare const ReportPresentationSchema: z.ZodObject<{
282
+ archetype: z.ZodEnum<{
283
+ "token-protocol": "token-protocol";
284
+ "wallet-flow": "wallet-flow";
285
+ "contract-security": "contract-security";
286
+ "market-liquidity": "market-liquidity";
287
+ "governance-treasury": "governance-treasury";
288
+ "monitoring-events": "monitoring-events";
289
+ }>;
290
+ agentName: z.ZodOptional<z.ZodString>;
291
+ headline: z.ZodNullable<z.ZodObject<{
292
+ section: z.ZodString;
293
+ finding: z.ZodString;
294
+ }, z.core.$strict>>;
295
+ blocks: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
296
+ type: z.ZodLiteral<"metrics">;
297
+ title: z.ZodString;
298
+ items: z.ZodArray<z.ZodObject<{
299
+ section: z.ZodString;
300
+ finding: z.ZodString;
301
+ }, z.core.$strict>>;
302
+ }, z.core.$strict>, z.ZodObject<{
303
+ type: z.ZodLiteral<"section">;
304
+ section: z.ZodString;
305
+ }, z.core.$strict>, z.ZodObject<{
306
+ type: z.ZodLiteral<"table">;
307
+ dataset: z.ZodString;
308
+ title: z.ZodOptional<z.ZodString>;
309
+ }, z.core.$strict>, z.ZodObject<{
310
+ type: z.ZodLiteral<"bars">;
311
+ dataset: z.ZodString;
312
+ label: z.ZodString;
313
+ value: z.ZodString;
314
+ title: z.ZodOptional<z.ZodString>;
315
+ }, z.core.$strict>, z.ZodObject<{
316
+ type: z.ZodLiteral<"timeline">;
317
+ dataset: z.ZodString;
318
+ at: z.ZodString;
319
+ label: z.ZodString;
320
+ detail: z.ZodOptional<z.ZodString>;
321
+ title: z.ZodOptional<z.ZodString>;
322
+ }, z.core.$strict>, z.ZodObject<{
323
+ type: z.ZodLiteral<"flow">;
324
+ dataset: z.ZodString;
325
+ from: z.ZodString;
326
+ to: z.ZodString;
327
+ amount: z.ZodOptional<z.ZodString>;
328
+ title: z.ZodOptional<z.ZodString>;
329
+ }, z.core.$strict>, z.ZodObject<{
330
+ type: z.ZodLiteral<"graph">;
331
+ dataset: z.ZodString;
332
+ to: z.ZodString;
333
+ relation: z.ZodOptional<z.ZodString>;
334
+ from: z.ZodOptional<z.ZodString>;
335
+ title: z.ZodOptional<z.ZodString>;
336
+ }, z.core.$strict>, z.ZodObject<{
337
+ type: z.ZodLiteral<"signals">;
338
+ title: z.ZodOptional<z.ZodString>;
339
+ }, z.core.$strict>, z.ZodObject<{
340
+ type: z.ZodLiteral<"coverage">;
341
+ title: z.ZodOptional<z.ZodString>;
342
+ }, z.core.$strict>, z.ZodObject<{
343
+ type: z.ZodLiteral<"limitations">;
344
+ title: z.ZodOptional<z.ZodString>;
345
+ }, z.core.$strict>], "type">>;
346
+ }, z.core.$strict>;
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Shared report presentation (R01). Every AGENTEX report renders in one shell (Overview, Findings, Evidence, Sources, Execution, Export);
4
+ * the archetype and blocks choose how its content is laid out. A workflow version declares them in its manifest: which tool outputs become
5
+ * bounded tables (`datasets`), which tool observations are carried into the report (`signals`), which log windows state coverage, and the
6
+ * ordered blocks that render them. Presentation is layout only: it never grants a capability, adds a read or changes a finding, and every
7
+ * row it carries keeps the evidence of the step that produced it. Fixed first-party kinds map their reports to the same blocks in the web.
8
+ */
9
+ export const REPORT_ARCHETYPES = ['token-protocol', 'wallet-flow', 'contract-security', 'market-liquidity', 'governance-treasury', 'monitoring-events'];
10
+ export const ReportArchetypeSchema = z.enum(REPORT_ARCHETYPES);
11
+ const Identifier = z.string().regex(/^[a-z][a-z0-9._-]{0,63}$/);
12
+ /** A dot path into a tool output (`summary.poolsText`, `pools`, `largestTransfers.0`). Empty means the value itself. */
13
+ const OutputPath = z.string().max(120).regex(/^([A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*)?$/);
14
+ const Title = z.string().min(1).max(80);
15
+ /** Column keys may be camelCase (they name output fields). */
16
+ const ColumnKey = z.string().regex(/^[a-z][a-zA-Z0-9_-]{0,39}$/);
17
+ /** Column types decide formatting only (addresses shorten and link, amounts keep their exact text); values are never converted. */
18
+ export const DatasetColumnTypeSchema = z.enum(['text', 'label', 'address', 'amount', 'integer', 'percent', 'block', 'transaction', 'boolean', 'time']);
19
+ export const DatasetSpecSchema = z.strictObject({
20
+ id: Identifier, title: Title, node: Identifier, path: OutputPath,
21
+ columns: z.array(z.strictObject({ key: ColumnKey, label: z.string().min(1).max(60), type: DatasetColumnTypeSchema, path: OutputPath })).min(1).max(8),
22
+ maxRows: z.number().int().min(1).max(25),
23
+ });
24
+ const FindingRef = z.strictObject({ section: Identifier, finding: Identifier });
25
+ const DatasetColumnRef = ColumnKey;
26
+ export const ReportBlockSchema = z.discriminatedUnion('type', [
27
+ /** Headline figures: up to eight findings shown as a metric grid, each with its evidence. */
28
+ z.strictObject({ type: z.literal('metrics'), title: Title, items: z.array(FindingRef).min(1).max(8) }),
29
+ /** One declared section as a labelled list of its findings. */
30
+ z.strictObject({ type: z.literal('section'), section: Identifier }),
31
+ z.strictObject({ type: z.literal('table'), dataset: Identifier, title: Title.optional() }),
32
+ /** Horizontal bars of one numeric column per row (amounts and percentages keep their exact text beside the bar). */
33
+ z.strictObject({ type: z.literal('bars'), dataset: Identifier, label: DatasetColumnRef, value: DatasetColumnRef, title: Title.optional() }),
34
+ /** Rows ordered by a block or integer column. */
35
+ z.strictObject({ type: z.literal('timeline'), dataset: Identifier, at: DatasetColumnRef, label: DatasetColumnRef, detail: DatasetColumnRef.optional(), title: Title.optional() }),
36
+ /** Movements between two parties per row, with an optional amount. */
37
+ z.strictObject({ type: z.literal('flow'), dataset: Identifier, from: DatasetColumnRef, to: DatasetColumnRef, amount: DatasetColumnRef.optional(), title: Title.optional() }),
38
+ /** Entity relationships: the report subject (or a `from` column) linked to each row's `to` entity by `relation`. */
39
+ z.strictObject({ type: z.literal('graph'), dataset: Identifier, to: DatasetColumnRef, relation: DatasetColumnRef.optional(), from: DatasetColumnRef.optional(), title: Title.optional() }),
40
+ z.strictObject({ type: z.literal('signals'), title: Title.optional() }),
41
+ z.strictObject({ type: z.literal('coverage'), title: Title.optional() }),
42
+ z.strictObject({ type: z.literal('limitations'), title: Title.optional() }),
43
+ ]);
44
+ export const WorkflowPresentationSchema = z.strictObject({
45
+ archetype: ReportArchetypeSchema,
46
+ /** The finding that states the result in one sentence; it leads the Overview. */
47
+ headline: FindingRef.optional(),
48
+ datasets: z.array(DatasetSpecSchema).max(8).optional(),
49
+ /** Tool nodes whose own observations (code, message, evidence) are carried into the report as signals. */
50
+ signals: z.array(Identifier).max(6).optional(),
51
+ /** Tool outputs holding a bounded block window (`coverage` of log-reading tools), shown as stated coverage. */
52
+ coverage: z.array(z.strictObject({ node: Identifier, path: OutputPath, label: z.string().min(1).max(60) })).max(4).optional(),
53
+ blocks: z.array(ReportBlockSchema).min(1).max(14),
54
+ });
55
+ /** Reference checks against the manifest: nodes are tool nodes, datasets and columns exist, findings are declared. */
56
+ export function presentationIssues(presentation, manifest) {
57
+ const issues = [];
58
+ const at = (...path) => ['presentation', ...path];
59
+ const toolNodes = new Set(manifest.workflow.filter((node) => node.type === 'tool').map((node) => node.id));
60
+ const findingExists = (ref) => manifest.outputSchema.sections.some((section) => section.id === ref.section && section.findings.some((item) => item.code === ref.finding));
61
+ const declared = presentation.datasets ?? [];
62
+ const signals = presentation.signals ?? [];
63
+ const coverage = presentation.coverage ?? [];
64
+ const datasets = new Map(declared.map((dataset) => [dataset.id, dataset]));
65
+ if (datasets.size !== declared.length)
66
+ issues.push({ path: at('datasets'), message: 'Duplicate dataset id' });
67
+ declared.forEach((dataset, position) => {
68
+ if (!toolNodes.has(dataset.node))
69
+ issues.push({ path: at('datasets', position, 'node'), message: `Dataset ${dataset.id} must read a tool node` });
70
+ if (new Set(dataset.columns.map((column) => column.key)).size !== dataset.columns.length)
71
+ issues.push({ path: at('datasets', position, 'columns'), message: `Duplicate column in dataset ${dataset.id}` });
72
+ });
73
+ signals.forEach((node, position) => { if (!toolNodes.has(node))
74
+ issues.push({ path: at('signals', position), message: `Signals must come from a tool node (${node})` }); });
75
+ if (new Set(signals).size !== signals.length)
76
+ issues.push({ path: at('signals'), message: 'Duplicate signal node' });
77
+ coverage.forEach((item, position) => { if (!toolNodes.has(item.node))
78
+ issues.push({ path: at('coverage', position, 'node'), message: `Coverage must come from a tool node (${item.node})` }); });
79
+ if (presentation.headline && !findingExists(presentation.headline))
80
+ issues.push({ path: at('headline'), message: `Headline ${presentation.headline.section}.${presentation.headline.finding} is not a declared finding` });
81
+ presentation.blocks.forEach((block, position) => {
82
+ if (block.type === 'metrics')
83
+ block.items.forEach((item, index) => { if (!findingExists(item))
84
+ issues.push({ path: at('blocks', position, 'items', index), message: `Metric ${item.section}.${item.finding} is not a declared finding` }); });
85
+ if (block.type === 'section' && !manifest.outputSchema.sections.some((section) => section.id === block.section))
86
+ issues.push({ path: at('blocks', position, 'section'), message: `Section ${block.section} is not declared` });
87
+ if (block.type === 'signals' && !signals.length)
88
+ issues.push({ path: at('blocks', position), message: 'A signals block needs at least one signal node' });
89
+ if (block.type === 'coverage' && !coverage.length)
90
+ issues.push({ path: at('blocks', position), message: 'A coverage block needs at least one coverage source' });
91
+ if ('dataset' in block) {
92
+ const dataset = datasets.get(block.dataset);
93
+ if (!dataset) {
94
+ issues.push({ path: at('blocks', position, 'dataset'), message: `Dataset ${block.dataset} is not declared` });
95
+ return;
96
+ }
97
+ const columns = new Set(dataset.columns.map((column) => column.key));
98
+ for (const key of ['label', 'value', 'at', 'detail', 'from', 'to', 'amount', 'relation']) {
99
+ const ref = block[key];
100
+ if (typeof ref === 'string' && !columns.has(ref))
101
+ issues.push({ path: at('blocks', position, key), message: `Column ${ref} is not in dataset ${dataset.id}` });
102
+ }
103
+ }
104
+ });
105
+ return issues;
106
+ }
107
+ /** Report fields a presentation produces. Rows hold exact scalar values from the step output (null where the output has none). */
108
+ const Cell = z.union([z.string().max(200), z.number().finite(), z.boolean(), z.null()]);
109
+ export const ReportDatasetSchema = z.strictObject({
110
+ id: Identifier, title: Title, node: Identifier, status: z.enum(['observed', 'absent', 'unavailable']), reason: z.string().max(300).nullable(),
111
+ columns: z.array(z.strictObject({ key: ColumnKey, label: z.string().max(60), type: DatasetColumnTypeSchema })).max(8),
112
+ rows: z.array(z.array(Cell).max(8)).max(25), totalRows: z.number().int().nonnegative(), truncated: z.boolean(), evidenceIds: z.array(z.string()).max(64),
113
+ });
114
+ export const ReportSignalSchema = z.strictObject({ node: Identifier, code: z.string().max(64), message: z.string().max(600), evidenceIds: z.array(z.string()).max(16) });
115
+ export const ReportCoverageSchema = z.strictObject({
116
+ node: Identifier, label: z.string().max(60), status: z.enum(['observed', 'absent', 'unavailable']), fromBlock: z.string().max(24).nullable(), toBlock: z.string().max(24).nullable(),
117
+ blocks: z.number().int().nonnegative().nullable(), requestedBlocks: z.number().int().nonnegative().nullable(), complete: z.boolean().nullable(), reason: z.string().max(300).nullable(), approximateDuration: z.string().max(60).nullable(),
118
+ });
119
+ /** `agentName` is the listed version's name, so shared and saved reports can title themselves without the manifest. */
120
+ export const ReportPresentationSchema = z.strictObject({ archetype: ReportArchetypeSchema, agentName: z.string().max(100).optional(), headline: FindingRef.nullable(), blocks: z.array(ReportBlockSchema).max(14) });