pi-outpost 0.7.0 → 0.9.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/dist/contract/README.md +190 -0
- package/dist/contract/conformance/README.md +54 -0
- package/dist/contract/conformance/index.json +165 -0
- package/dist/contract/conformance/invalid/change-without-reference.json +17 -0
- package/dist/contract/conformance/invalid/change-without-target.json +10 -0
- package/dist/contract/conformance/invalid/container-as-endpoint.json +26 -0
- package/dist/contract/conformance/invalid/container-duplicate-id.json +24 -0
- package/dist/contract/conformance/invalid/container-unknown-membership.json +20 -0
- package/dist/contract/conformance/invalid/containers-over-the-ceiling.json +220 -0
- package/dist/contract/conformance/invalid/duplicate-identifier.json +17 -0
- package/dist/contract/conformance/invalid/duplicate-reference-twice.json +24 -0
- package/dist/contract/conformance/invalid/duplicate-reference.json +23 -0
- package/dist/contract/conformance/invalid/empty-change.json +15 -0
- package/dist/contract/conformance/invalid/kind-data-mismatch.json +13 -0
- package/dist/contract/conformance/invalid/label-past-ceiling.json +13 -0
- package/dist/contract/conformance/invalid/new-element-without-label.json +12 -0
- package/dist/contract/conformance/invalid/new-relationship-without-kind.json +22 -0
- package/dist/contract/conformance/invalid/relationship-change-without-target.json +13 -0
- package/dist/contract/conformance/invalid/relationship-without-endpoints.json +19 -0
- package/dist/contract/conformance/invalid/removal-without-target.json +19 -0
- package/dist/contract/conformance/invalid/removal-without-type.json +19 -0
- package/dist/contract/conformance/invalid/row-column-mismatch.json +15 -0
- package/dist/contract/conformance/invalid/table-with-empty-removals.json +6 -0
- package/dist/contract/conformance/invalid/table-with-removal.json +20 -0
- package/dist/contract/conformance/invalid/table-with-target.json +15 -0
- package/dist/contract/conformance/invalid/too-many-element-kinds.json +340 -0
- package/dist/contract/conformance/invalid/too-many-relationship-kinds.json +665 -0
- package/dist/contract/conformance/invalid/undeclared-property.json +14 -0
- package/dist/contract/conformance/invalid/unknown-version.json +13 -0
- package/dist/contract/conformance/invalid/unresolved-endpoint.json +19 -0
- package/dist/contract/conformance/valid/graph-at-the-kind-ceiling.json +650 -0
- package/dist/contract/conformance/valid/graph-containers-at-the-ceiling.json +216 -0
- package/dist/contract/conformance/valid/graph-minimal.json +13 -0
- package/dist/contract/conformance/valid/graph-proposal-addition-only.json +14 -0
- package/dist/contract/conformance/valid/graph-proposal-context-element.json +24 -0
- package/dist/contract/conformance/valid/graph-proposal-declared-change.json +18 -0
- package/dist/contract/conformance/valid/graph-proposal-field-patch.json +17 -0
- package/dist/contract/conformance/valid/graph-proposal-moves-a-member.json +28 -0
- package/dist/contract/conformance/valid/graph-proposal-named-context.json +25 -0
- package/dist/contract/conformance/valid/graph-proposal-reattachment.json +30 -0
- package/dist/contract/conformance/valid/graph-with-containers.json +55 -0
- package/dist/contract/conformance/valid/graph-with-empty-container.json +24 -0
- package/dist/contract/conformance/valid/graph-with-typed-relationship.json +24 -0
- package/dist/contract/conformance/valid/sequence-minimal.json +13 -0
- package/dist/contract/conformance/valid/sequence-ordered.json +28 -0
- package/dist/contract/conformance/valid/sequence-with-containers.json +45 -0
- package/dist/contract/conformance/valid/table-minimal.json +20 -0
- package/dist/contract/schemas/structured-exchange-1.json +368 -0
- package/dist/contract/validate-structured-exchange.mjs +8670 -0
- package/dist/pi-outpost.mjs +13091 -3628
- package/dist/pi-outpost.sea.mjs +17422 -7961
- package/dist/sea-prep.blob +0 -0
- package/dist/skills/README.md +22 -0
- package/dist/skills/structured-exchange/SKILL.md +256 -0
- package/dist/skills/structured-exchange/structured-exchange-1.json +368 -0
- package/dist/web/assets/{PdfViewer-DqktVuQi.js → PdfViewer-CEAp6biY.js} +2 -2
- package/dist/web/assets/{abnfDiagram-N423BO3Z-DTFllthO.js → abnfDiagram-N423BO3Z-BE4AMXSH.js} +1 -1
- package/dist/web/assets/architecture-TIHT7OUA-DgTbwInB.js +1 -0
- package/dist/web/assets/{architectureDiagram-T3A2C74G-z58o8Htx.js → architectureDiagram-T3A2C74G-tjG-eidb.js} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-DunlRCsF.js → blockDiagram-VBNYF7ZC-Bwzla1cD.js} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-BnXZSwmd.js → c4Diagram-5PPSVZJV-BbNH9mqH.js} +1 -1
- package/dist/web/assets/channel-CXbqHDJA.js +1 -0
- package/dist/web/assets/{chunk-2GRJ4B5K-DMD6wv-7.js → chunk-2GRJ4B5K-BTjPaaWT.js} +1 -1
- package/dist/web/assets/{chunk-3NCLNEKW-DcwPPr-p.js → chunk-3NCLNEKW-DfGGO5Az.js} +1 -1
- package/dist/web/assets/{chunk-4I5QYGJK-CpI4oXUq.js → chunk-4I5QYGJK-Bajr_p-3.js} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-vLnKsYcD.js → chunk-5RXB4S5H-CbDXtSwX.js} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-C2P_VKtw.js → chunk-6Q2QTUOP-Cj5IB9-q.js} +1 -1
- package/dist/web/assets/{chunk-7Z6QIM7H-9Vj99wwd.js → chunk-7Z6QIM7H-BIsTfYEd.js} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-Cna6JsDy.js → chunk-GF5L2VYU-CYLPBeKA.js} +1 -1
- package/dist/web/assets/{chunk-I66GZJ75-Crrt_0-l.js → chunk-I66GZJ75-BjZt7BZl.js} +3 -3
- package/dist/web/assets/{chunk-J7OUQ5F2-NnE94fab.js → chunk-J7OUQ5F2-B3gViRUp.js} +2 -2
- package/dist/web/assets/{chunk-JQJVKLGR-DV6jrsII.js → chunk-JQJVKLGR-Dg177seh.js} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-96NXxGM6.js → chunk-KBJHAD2P-B_2jByMg.js} +1 -1
- package/dist/web/assets/{chunk-NSK5VX7P-D8DkQ2AO.js → chunk-NSK5VX7P-B5w-3_Ed.js} +1 -1
- package/dist/web/assets/{chunk-QR6OTTB3-BCLPnKlE.js → chunk-QR6OTTB3-DCJRxmQT.js} +1 -1
- package/dist/web/assets/{chunk-UBXNYLIW-DswC9lJa.js → chunk-UBXNYLIW-DBuIxFzr.js} +1 -1
- package/dist/web/assets/{chunk-W5SLKNZC-CPDEB8Hp.js → chunk-W5SLKNZC-B-cGIOQF.js} +1 -1
- package/dist/web/assets/{chunk-WRU74C26-DCZH8Rwq.js → chunk-WRU74C26-CLOxZV1w.js} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-lv93WkUc.js +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-lv93WkUc.js +1 -0
- package/dist/web/assets/{cynefin-VYW2F7L2-cIE_7dpt.js → cynefin-VYW2F7L2-Bk96SN7L.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-D2MrYar5.js → cynefinDiagram-MW4NZA55-ooeswKGp.js} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-BagJQZYc.js → dagre-VZM6K2ZE-CSwH_rTA.js} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-BwKHjjQ-.js → diagram-7IWD3JNH-DVtrh_uV.js} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-BJmMpny4.js → diagram-B4RE2ZJO-CYE5VmiD.js} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-D3PE9vwZ.js → diagram-LBJQPF4R-BYOlHlmg.js} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-oiQW2yMD.js → diagram-Q27KOJAE-DJWxpvu9.js} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-lKRQ-OEd.js → diagram-UB23O5K3-B1g-vwj0.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-B7T7YLs8.js → ebnfDiagram-BXEA7PRR-BX8Jv2Vd.js} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-BB-xsqVL.js → erDiagram-JOGREHBK-9hxklGu2.js} +1 -1
- package/dist/web/assets/eventmodeling-45OFAUF4-CSvMsqcO.js +1 -0
- package/dist/web/assets/flowDiagram-UKHOOZJN-CPmAOaGQ.js +1 -0
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-BnmC1zyp.js → ganttDiagram-PKOTCBZU-D0kAnvoM.js} +1 -1
- package/dist/web/assets/{gitGraph-TEB2WS4Q-DFCnvCQm.js → gitGraph-TEB2WS4Q-C9UMPCgj.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-CVU7rVHy.js → gitGraphDiagram-DS77QQ5N-6tPgqwE6.js} +1 -1
- package/dist/web/assets/index-CZwzQTfU.css +2 -0
- package/dist/web/assets/index-De86t3J_.js +323 -0
- package/dist/web/assets/{info-DKCQHKI2-C8DZu4NS.js → info-DKCQHKI2-BBV4qqmE.js} +1 -1
- package/dist/web/assets/{infoDiagram-6WML65LV-BSYvkU5b.js → infoDiagram-6WML65LV-Bmhc86CN.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-B6uWyt8M.js → ishikawaDiagram-WSZJBQD7-5wn4Yv2C.js} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-CTrfVU7d.js → journeyDiagram-NVQOT4AX-DJLLpQlS.js} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DsBfco54.js → kanban-definition-27J2QSJJ-7h9EMbUW.js} +1 -1
- package/dist/web/assets/{line-C6m1STwY.js → line-Uu7s3CmK.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-DbS7gJe7.js → mermaid-parser.core-DwpLoctN.js} +3 -3
- package/dist/web/assets/{mermaid.core-Blq4BYkb.js → mermaid.core-CRSZr8Y7.js} +3 -3
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-BWL0h6Uq.js → mindmap-definition-FAOFIHXS-DPic1KLK.js} +1 -1
- package/dist/web/assets/{packet-7NZHBO7P-CryH8SNn.js → packet-7NZHBO7P-CMRjleUu.js} +1 -1
- package/dist/web/assets/{pdf-BxEVw97_.js → pdf-DGJrgWix.js} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-CWrJoBL8.js → pegDiagram-VL7TDLO6-CdECC4cs.js} +1 -1
- package/dist/web/assets/{pie-RZYD4A2V-DsuD2v_v.js → pie-RZYD4A2V-C5X4oGn0.js} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-D6-xXQ5p.js → pieDiagram-7S7Q4E2Y-Bvs4Q2O-.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-CMlYXuHz.js → quadrantDiagram-CIZ2JOQS-FsUrYDg7.js} +1 -1
- package/dist/web/assets/{radar-I7S5WNFK-CjUoPns1.js → radar-I7S5WNFK-tvx7HrZO.js} +1 -1
- package/dist/web/assets/{railroad-3IZDKUUU-Dv7YQd2U.js → railroad-3IZDKUUU-DBks1QD-.js} +1 -1
- package/dist/web/assets/railroad-abnf-AHOZXSZD-BnvB29Nv.js +1 -0
- package/dist/web/assets/railroad-ebnf-EBAXGLYW-BUSDQ0vn.js +1 -0
- package/dist/web/assets/railroad-peg-LSFZ7HO6-CZV0givf.js +1 -0
- package/dist/web/assets/{railroadDiagram-AXF67PYL-bzfj9r2a.js → railroadDiagram-AXF67PYL-CBvFYCmN.js} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-B7PaiV8r.js → requirementDiagram-LRYGKXZP-C-rumttI.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-DEKRi7gf.js → sankeyDiagram-W5VNT64P-ClOK2cdj.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-BB2FYR31.js → sequenceDiagram-SI44F4Z6-B0pKllVj.js} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-BnQwJgUG.js → stateDiagram-OKZ733FA-BoAWywzk.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CHrsco-w.js +1 -0
- package/dist/web/assets/{swimlanes-SLNWSIFB-omoGGO13.js → swimlanes-SLNWSIFB-cdgjeXSf.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DPjAUQdL.js +8 -0
- package/dist/web/assets/{timeline-definition-Z64GVDOM-D0C35XAV.js → timeline-definition-Z64GVDOM-ByMPGIEh.js} +1 -1
- package/dist/web/assets/{treeView-QDETBFTQ-D21D9o06.js → treeView-QDETBFTQ-zBqwQF47.js} +1 -1
- package/dist/web/assets/{treemap-6X3UGDF4-DsNn3HqU.js → treemap-6X3UGDF4-C2U9S2di.js} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-Cg1oGxJi.js → vennDiagram-T6HMQDX7-CyJfOcB0.js} +1 -1
- package/dist/web/assets/{wardley-OPB4EBWU-Df-Ugyh4.js → wardley-OPB4EBWU-CDxzQuvF.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-DKiQg0PU.js → wardleyDiagram-T6FBY63Y-IO8_CDTG.js} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-DecoU60Y.js → xychartDiagram-ELKLHX3M-UUfxFEDc.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/web/assets/architecture-TIHT7OUA-CfWIx3bb.js +0 -1
- package/dist/web/assets/channel-By9fNdgM.js +0 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-CbRWDDDS.js +0 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-CbRWDDDS.js +0 -1
- package/dist/web/assets/eventmodeling-45OFAUF4-CIh5g1Zv.js +0 -1
- package/dist/web/assets/flowDiagram-UKHOOZJN-DWTzsyQN.js +0 -1
- package/dist/web/assets/index-Ct33KYcg.js +0 -321
- package/dist/web/assets/index-DFy_CqqQ.css +0 -2
- package/dist/web/assets/railroad-abnf-AHOZXSZD-Cr90hvNI.js +0 -1
- package/dist/web/assets/railroad-ebnf-EBAXGLYW-BhLKG67T.js +0 -1
- package/dist/web/assets/railroad-peg-LSFZ7HO6-Ct2bnVSV.js +0 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CsdKarH7.js +0 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-KAs6WWZH.js +0 -8
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Structured exchange — for producers
|
|
2
|
+
|
|
3
|
+
A tool can return structured data alongside its text, and this application will render
|
|
4
|
+
it natively: a graph, a sequence, or a table, drawn from the data rather than from
|
|
5
|
+
anything the tool wrote for display. When the document names a target it is read as a
|
|
6
|
+
*proposal* to change something an external authority holds, and its rendering becomes
|
|
7
|
+
the approval gate before that change is applied.
|
|
8
|
+
|
|
9
|
+
This page is for whoever writes such a producer. The normative contract is
|
|
10
|
+
[`shared/schemas/structured-exchange-1.json`](../shared/schemas/structured-exchange-1.json).
|
|
11
|
+
|
|
12
|
+
## The two payloads, and why you owe both
|
|
13
|
+
|
|
14
|
+
A tool result carries two things, and they go to different readers:
|
|
15
|
+
|
|
16
|
+
| Channel | Reaches | Carries |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| the result's text content | **the model** | what the agent will reason about later |
|
|
19
|
+
| `details` | **the interface only** | the structured document |
|
|
20
|
+
|
|
21
|
+
The SDK defines `details` as metadata the LLM does not see. That is what makes it the
|
|
22
|
+
right home for a 500-element graph — the model pays nothing for it — and it is exactly
|
|
23
|
+
why the text half is not optional.
|
|
24
|
+
|
|
25
|
+
**A producer that emits only the structured document leaves the agent with nothing to
|
|
26
|
+
reason about.** It will render beautifully and be useless on the next turn, when the
|
|
27
|
+
agent is asked a follow-up question about a structure it cannot see. Summarise the
|
|
28
|
+
structure in the text: what it contains, what changed, what matters.
|
|
29
|
+
|
|
30
|
+
The reverse also holds: text alone gets you today's behaviour, a wall of prose.
|
|
31
|
+
|
|
32
|
+
## Describing is not changing
|
|
33
|
+
|
|
34
|
+
On anything carrying a `ref`, the fields you declare beside it **describe what the
|
|
35
|
+
authority already holds**. They are how a reader recognises the thing. They are not
|
|
36
|
+
applied. An intended change goes in `set`:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{ "id": "ledger", "ref": "EL-7", "label": "Ledger",
|
|
40
|
+
"set": { "label": "General Ledger" } }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Two consequences worth stating plainly.
|
|
44
|
+
|
|
45
|
+
**Include as much context as the reader needs.** Elements carried purely so the
|
|
46
|
+
proposal can be situated cost nothing and change nothing. A proposal nobody can place
|
|
47
|
+
is a proposal nobody should approve.
|
|
48
|
+
|
|
49
|
+
**The default runs this way round because producers forget.** Were a declared field
|
|
50
|
+
taken as an intended change, a producer including twenty elements for context would be
|
|
51
|
+
proposing twenty renames to the names those elements already have — and the reader,
|
|
52
|
+
seeing them marked as changes, could approve them. This way a forgotten `set` changes
|
|
53
|
+
nothing and somebody says "it didn't work". A generative producer's mistakes have to
|
|
54
|
+
fail inert.
|
|
55
|
+
|
|
56
|
+
A `set` on something with no `ref` is refused: there is nothing there to change, and
|
|
57
|
+
its fields are already its values.
|
|
58
|
+
|
|
59
|
+
## Emitting a document
|
|
60
|
+
|
|
61
|
+
Put the envelope in your tool result's `details`:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: "text", text: "Billing now calls Ledger. 12 elements, 1 added." }],
|
|
66
|
+
details: {
|
|
67
|
+
schema: "urn:structured-exchange:1",
|
|
68
|
+
kind: "graph",
|
|
69
|
+
data: {
|
|
70
|
+
nodes: [{ id: "billing", label: "Billing" }, { id: "ledger", label: "Ledger" }],
|
|
71
|
+
edges: [{ from: "billing", to: "ledger", kind: "calls" }],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The server forwards anything whose `schema` starts with `urn:structured-exchange:`
|
|
78
|
+
and validates nothing — validation happens where the rendering decision is made.
|
|
79
|
+
|
|
80
|
+
## The agent as a producer
|
|
81
|
+
|
|
82
|
+
The agent can author these too, guided by [`skills/structured-exchange`](../skills/structured-exchange/SKILL.md).
|
|
83
|
+
It presents one through the `present_structure` tool, which validates before showing
|
|
84
|
+
anything and hands back the diagnostics when it refuses, so a document can be corrected
|
|
85
|
+
without leaving the exchange.
|
|
86
|
+
|
|
87
|
+
## Validating before you emit
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
node contract/validate-structured-exchange.mjs document.json
|
|
91
|
+
cat document.json | node contract/validate-structured-exchange.mjs
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
One file, no install, no checkout: the schema and the rules are inside it. In this
|
|
95
|
+
repository, build it with `npm run build:validator` and find it at
|
|
96
|
+
`shared/dist/validate-structured-exchange.mjs`.
|
|
97
|
+
|
|
98
|
+
| Exit | Meaning |
|
|
99
|
+
|------|---------|
|
|
100
|
+
| `0` | the document conforms |
|
|
101
|
+
| `1` | the document was read and parsed, and does not conform |
|
|
102
|
+
| `2` | the input could not be read at all |
|
|
103
|
+
| `3` | the input was read and is not JSON |
|
|
104
|
+
|
|
105
|
+
The last three are separated on purpose. A missing file, a truncated write and a
|
|
106
|
+
document that says the wrong thing send you looking in three different places, and a
|
|
107
|
+
build that collapses them into "invalid" sends you to the schema for a problem that
|
|
108
|
+
is not there.
|
|
109
|
+
|
|
110
|
+
Diagnostics name the rule and point at the value:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{"valid": false, "issues": [
|
|
114
|
+
{"rule": "unresolved-endpoint", "path": "/data/edges/0/to",
|
|
115
|
+
"message": "\"ledgr\" is not an identifier declared in /data/nodes"}
|
|
116
|
+
]}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Every broken rule is reported, not just the first.
|
|
120
|
+
|
|
121
|
+
## Getting a diagram into a document
|
|
122
|
+
|
|
123
|
+
Use **download SVG**, then insert the file as a picture. Word does not accept an SVG
|
|
124
|
+
pasted from the clipboard — it wants a file. **copy markup** is there for the places
|
|
125
|
+
that do take it directly: an editor, a wiki, a repository.
|
|
126
|
+
|
|
127
|
+
The markup stands on its own. Boxes are `rect` and `text` with colours as attributes
|
|
128
|
+
and an explicit white ground, so what lands in the document is what was on screen. An
|
|
129
|
+
earlier version drew them as HTML inside `foreignObject`, which looks identical in the
|
|
130
|
+
browser and loses everything the moment it is serialized.
|
|
131
|
+
|
|
132
|
+
## If you are not building in this repository
|
|
133
|
+
|
|
134
|
+
You do not need our command-line interface, and you do not need this repository. The
|
|
135
|
+
contract ships with the package, under `contract/`:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
node_modules/pi-outpost/dist/contract/
|
|
139
|
+
schemas/structured-exchange-1.json the normative schema — any validator runs it
|
|
140
|
+
conformance/ documents and the verdict each should get
|
|
141
|
+
validate-structured-exchange.mjs the reference validator, self-contained
|
|
142
|
+
README.md this page
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- The **schema** is what the application validates against, byte for byte: it is the
|
|
146
|
+
same file, copied at build time rather than restated.
|
|
147
|
+
- The **conformance suite** covers the relational rules JSON Schema cannot express.
|
|
148
|
+
Run your implementation against it; if it agrees on every case, it conforms.
|
|
149
|
+
- The **validator** is the reference implementation of both, bundled with everything
|
|
150
|
+
it needs. Use it as a check on your own, or as the check itself.
|
|
151
|
+
|
|
152
|
+
In this repository the same two live at `shared/schemas/` and `shared/conformance/`.
|
|
153
|
+
|
|
154
|
+
## Where the document has to be put
|
|
155
|
+
|
|
156
|
+
On `details` of a tool result. That is the whole channel, and it is deliberate:
|
|
157
|
+
`details` is filled by a tool's implementation and never by the model, so a proposal
|
|
158
|
+
shown as an approval gate was produced by code rather than written by the thing whose
|
|
159
|
+
work is being reviewed.
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
return {
|
|
163
|
+
content: [{ type: "text", text: "Billing now calls Ledger. 12 elements, 1 added." }],
|
|
164
|
+
details: envelope,
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**There is no MCP path.** The agent SDK underneath has no MCP client — it says so
|
|
169
|
+
outright and points anyone who wants one at writing an extension. So if you are
|
|
170
|
+
bridging a model-context server into this, the bridge is yours to write, and it meets
|
|
171
|
+
this contract by returning the envelope in `details` of its own tool result. Nothing
|
|
172
|
+
here reads MCP's `structuredContent`, because nothing in this process produces it.
|
|
173
|
+
|
|
174
|
+
Relay it unchanged. A bridge that reshapes what it passes through is a second
|
|
175
|
+
producer, and the reader would be approving its work rather than the original.
|
|
176
|
+
|
|
177
|
+
## What is deliberately not here
|
|
178
|
+
|
|
179
|
+
**Delivery.** How an approved proposal reaches the authority that applies it, and what
|
|
180
|
+
that authority reports back, is a separate contract. What this one guarantees is that
|
|
181
|
+
an approved proposal survives unaltered and can be recovered exactly as it was
|
|
182
|
+
validated — the precondition any delivery mechanism needs.
|
|
183
|
+
|
|
184
|
+
**Concurrency.** A document names *which* artifact it targets, not which revision. A
|
|
185
|
+
proposal built from a stale export and applied late is the receiving authority's to
|
|
186
|
+
detect; this contract does not carry what it would need to do so.
|
|
187
|
+
|
|
188
|
+
**A vocabulary.** Relationship kinds are opaque strings. What `calls`, `composition`,
|
|
189
|
+
or anything else means belongs to your domain, and enumerating it here would make a
|
|
190
|
+
provider-neutral contract into somebody's particular one.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Structured-exchange conformance suite
|
|
2
|
+
|
|
3
|
+
The executable half of the contract. `../schemas/structured-exchange-1.json` says what
|
|
4
|
+
the shape is; these cases say what a correct implementation *does* with documents that
|
|
5
|
+
sit on either side of the line — including the ones JSON Schema alone cannot judge.
|
|
6
|
+
|
|
7
|
+
Any producer, in any language, can run this: the cases are plain JSON and
|
|
8
|
+
`index.json` states the expected verdict for each. Nothing here depends on this
|
|
9
|
+
repository, which is the point — a producer that has to read our TypeScript to know
|
|
10
|
+
whether it conforms does not have a contract, it has a dependency.
|
|
11
|
+
|
|
12
|
+
## Layout
|
|
13
|
+
|
|
14
|
+
- `valid/` — documents a conforming implementation accepts.
|
|
15
|
+
- `invalid/` — documents it refuses. `index.json` names the rule each one breaks.
|
|
16
|
+
- `index.json` — the manifest, with `expectedRule` for every invalid case.
|
|
17
|
+
|
|
18
|
+
## Rules that are not in the schema
|
|
19
|
+
|
|
20
|
+
JSON Schema decides shape. These are the relational rules that follow it, and the
|
|
21
|
+
`expectedRule` values that name them:
|
|
22
|
+
|
|
23
|
+
| Rule | What it refuses |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `duplicate-identifier` | two elements sharing an envelope-scoped `id` |
|
|
26
|
+
| `unresolved-endpoint` | a relationship endpoint that no declared element carries |
|
|
27
|
+
| `kind-data-mismatch` | a declared `kind` that disagrees with the data variant present |
|
|
28
|
+
| `kind-not-proposable` | a `table` carrying a `target` or a `removal` |
|
|
29
|
+
| `removal-without-target` | a removal in a document that targets nothing |
|
|
30
|
+
| `duplicate-reference` | the same reference addressed twice — changed twice, or changed and removed |
|
|
31
|
+
| `row-column-mismatch` | a row whose length differs from the declared columns |
|
|
32
|
+
|
|
33
|
+
Rules prefixed `schema/` come from the JSON Schema itself; the suffix is the keyword
|
|
34
|
+
that refused it.
|
|
35
|
+
|
|
36
|
+
## Running it here
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
node --import tsx/esm shared/bin/validate-structured-exchange.mjs shared/conformance/valid/graph-minimal.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Exits 0 for a valid document, 1 for a refused one, 2 when the input could not be read
|
|
43
|
+
at all — which is not the same thing as invalid, and a producer's build should not
|
|
44
|
+
treat it as if it were.
|
|
45
|
+
|
|
46
|
+
## Two things a conforming implementation must not do
|
|
47
|
+
|
|
48
|
+
**Repair.** No case here is close enough to valid to be worth guessing at, and that is
|
|
49
|
+
deliberate: `unresolved-endpoint` names an endpoint one character from a declared
|
|
50
|
+
identifier. Correcting it would produce a document the producer never wrote.
|
|
51
|
+
|
|
52
|
+
**Report only the first problem.** A document breaking several rules is reported
|
|
53
|
+
against all of them. A producer that has to fix one thing, be refused, fix the next,
|
|
54
|
+
and be refused again is a producer that stops using the format.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
{
|
|
2
|
+
"valid": [
|
|
3
|
+
{
|
|
4
|
+
"file": "valid/graph-minimal.json"
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"file": "valid/graph-with-typed-relationship.json"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"file": "valid/graph-proposal-addition-only.json"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"file": "valid/graph-proposal-field-patch.json"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"file": "valid/graph-proposal-context-element.json"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"file": "valid/graph-proposal-reattachment.json"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"file": "valid/sequence-minimal.json"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"file": "valid/sequence-ordered.json"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"file": "valid/table-minimal.json"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"file": "valid/graph-proposal-named-context.json"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"file": "valid/graph-proposal-declared-change.json"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"file": "valid/graph-at-the-kind-ceiling.json"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"file": "valid/graph-with-containers.json"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"file": "valid/sequence-with-containers.json"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"file": "valid/graph-with-empty-container.json"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"file": "valid/graph-proposal-moves-a-member.json"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"file": "valid/graph-containers-at-the-ceiling.json"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"invalid": [
|
|
56
|
+
{
|
|
57
|
+
"file": "invalid/unknown-version.json",
|
|
58
|
+
"expectedRule": "schema/const"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"file": "invalid/undeclared-property.json",
|
|
62
|
+
"expectedRule": "schema/additionalProperties"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"file": "invalid/new-element-without-label.json",
|
|
66
|
+
"expectedRule": "schema/required"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"file": "invalid/new-relationship-without-kind.json",
|
|
70
|
+
"expectedRule": "schema/required"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"file": "invalid/relationship-without-endpoints.json",
|
|
74
|
+
"expectedRule": "schema/required"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"file": "invalid/removal-without-type.json",
|
|
78
|
+
"expectedRule": "schema/required"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"file": "invalid/duplicate-identifier.json",
|
|
82
|
+
"expectedRule": "duplicate-identifier"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"file": "invalid/unresolved-endpoint.json",
|
|
86
|
+
"expectedRule": "unresolved-endpoint"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"file": "invalid/kind-data-mismatch.json",
|
|
90
|
+
"expectedRule": "kind-data-mismatch"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"file": "invalid/table-with-target.json",
|
|
94
|
+
"expectedRule": "kind-not-proposable"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"file": "invalid/table-with-removal.json",
|
|
98
|
+
"expectedRule": "kind-not-proposable"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"file": "invalid/removal-without-target.json",
|
|
102
|
+
"expectedRule": "removal-without-target"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"file": "invalid/duplicate-reference.json",
|
|
106
|
+
"expectedRule": "duplicate-reference"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"file": "invalid/row-column-mismatch.json",
|
|
110
|
+
"expectedRule": "row-column-mismatch"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"file": "invalid/label-past-ceiling.json",
|
|
114
|
+
"expectedRule": "schema/maxLength"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"file": "invalid/change-without-reference.json",
|
|
118
|
+
"expectedRule": "change-without-reference"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"file": "invalid/empty-change.json",
|
|
122
|
+
"expectedRule": "schema/minProperties"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"file": "invalid/duplicate-reference-twice.json",
|
|
126
|
+
"expectedRule": "duplicate-reference"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"file": "invalid/change-without-target.json",
|
|
130
|
+
"expectedRule": "change-without-target"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"file": "invalid/relationship-change-without-target.json",
|
|
134
|
+
"expectedRule": "change-without-target"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"file": "invalid/table-with-empty-removals.json",
|
|
138
|
+
"expectedRule": "kind-not-proposable"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"file": "invalid/too-many-element-kinds.json",
|
|
142
|
+
"expectedRule": "too-many-kinds"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"file": "invalid/too-many-relationship-kinds.json",
|
|
146
|
+
"expectedRule": "too-many-kinds"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"file": "invalid/container-unknown-membership.json",
|
|
150
|
+
"expectedRule": "unresolved-container"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"file": "invalid/container-duplicate-id.json",
|
|
154
|
+
"expectedRule": "duplicate-container-identifier"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"file": "invalid/container-as-endpoint.json",
|
|
158
|
+
"expectedRule": "unresolved-endpoint"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"file": "invalid/containers-over-the-ceiling.json",
|
|
162
|
+
"expectedRule": "schema/maxItems"
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "urn:structured-exchange:1",
|
|
3
|
+
"kind": "graph",
|
|
4
|
+
"data": {
|
|
5
|
+
"containers": [
|
|
6
|
+
{
|
|
7
|
+
"id": "electrical",
|
|
8
|
+
"label": "Electrical system"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"nodes": [
|
|
12
|
+
{
|
|
13
|
+
"id": "battery",
|
|
14
|
+
"label": "Battery",
|
|
15
|
+
"container": "electrical"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"edges": [
|
|
19
|
+
{
|
|
20
|
+
"from": "battery",
|
|
21
|
+
"to": "electrical",
|
|
22
|
+
"kind": "feeds"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "urn:structured-exchange:1",
|
|
3
|
+
"kind": "graph",
|
|
4
|
+
"data": {
|
|
5
|
+
"containers": [
|
|
6
|
+
{
|
|
7
|
+
"id": "electrical",
|
|
8
|
+
"label": "Electrical system"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "electrical",
|
|
12
|
+
"label": "Electrical system again"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"nodes": [
|
|
16
|
+
{
|
|
17
|
+
"id": "battery",
|
|
18
|
+
"label": "Battery",
|
|
19
|
+
"container": "electrical"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"edges": []
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "urn:structured-exchange:1",
|
|
3
|
+
"kind": "graph",
|
|
4
|
+
"data": {
|
|
5
|
+
"containers": [
|
|
6
|
+
{
|
|
7
|
+
"id": "electrical",
|
|
8
|
+
"label": "Electrical system"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"nodes": [
|
|
12
|
+
{
|
|
13
|
+
"id": "battery",
|
|
14
|
+
"label": "Battery",
|
|
15
|
+
"container": "hydraulic"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"edges": []
|
|
19
|
+
}
|
|
20
|
+
}
|