yarramate 0.18.0 → 0.19.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/README.md +2 -1
- package/catalogues/core-enrichment.yaml +52 -1
- package/dist/adapters/graphify-cli.js +0 -0
- package/dist/adapters/likec4-cli.js +0 -0
- package/dist/adapters/mcp-cli.js +0 -0
- package/dist/adapters/visual/client.js +0 -1
- package/dist/adapters/visual/protocol-contract.d.ts +118 -35
- package/dist/adapters/visual/protocol-contract.js +1 -11
- package/dist/adapters/visual/protocol.d.ts +8 -1
- package/dist/adapters/visual/protocol.js +86 -46
- package/dist/adapters/visual/request.d.ts +37 -0
- package/dist/adapters/visual/request.js +131 -0
- package/dist/adapters/visual/session-server.d.ts +12 -14
- package/dist/adapters/visual/session-server.js +641 -283
- package/dist/adapters/visual/session-store.d.ts +28 -28
- package/dist/adapters/visual/session-store.js +32 -93
- package/dist/adapters/visual/wire.d.ts +36 -10
- package/dist/adapters/visual-cli.d.ts +3 -0
- package/dist/adapters/visual-cli.js +66 -1
- package/dist/apply-command.d.ts +10 -0
- package/dist/apply-command.js +399 -247
- package/dist/cli.js +0 -0
- package/dist/compiler.d.ts +13 -0
- package/dist/compiler.js +36 -2
- package/dist/graph-projection.d.ts +62 -0
- package/dist/graph-projection.js +222 -0
- package/dist/interrogate-command.d.ts +2 -0
- package/dist/interrogate-command.js +22 -0
- package/dist/kind-label.d.ts +8 -0
- package/dist/kind-label.js +8 -0
- package/dist/operations.d.ts +139 -0
- package/dist/operations.js +11 -0
- package/dist/projection.d.ts +2 -0
- package/dist/projection.js +8 -2
- package/dist/reconciliation.js +8 -7
- package/dist/source-document.d.ts +10 -0
- package/dist/source-document.js +21 -1
- package/dist/visual-app/assets/index-6oSR9RGM.js +354 -0
- package/dist/visual-app/assets/index-dwA9AOr5.css +1 -0
- package/dist/visual-app/index.html +2 -2
- package/docs/CONSUMING-YARRAMATE.md +25 -19
- package/package.json +6 -1
- package/schema/yarramate-adapter-mapping.schema.json +1 -1
- package/schema/yarramate-apply-result.schema.json +8 -2
- package/schema/yarramate-document.schema.json +1 -1
- package/schema/yarramate-operations.schema.json +203 -1
- package/schema/yarramate-projection.schema.json +12 -3
- package/schema/yarramate-question-catalogue.schema.json +15 -2
- package/schema/yarramate-visual-event.schema.json +143 -0
- package/schema/yarramate-visual-graph.schema.json +272 -0
- package/schema/yarramate-visual-layout.schema.json +41 -0
- package/schema/yarramate-visual-model.schema.json +6 -44
- package/schema/yarramate-visual-response.schema.json +170 -19
- package/schema/yarramate-visual-session-descriptor.schema.json +1 -1
- package/schema/yarramate-visual-session-request.schema.json +0 -70
- package/schema/yarramate-visual-session-started.schema.json +2 -9
- package/schema/yarramate-visual-status.schema.json +3 -2
- package/skills/yarramate-architecture/SKILL.md +20 -13
- package/skills/yarramate-architecture/references/journey-checklists.md +4 -2
- package/skills/yarramate-architecture/references/native-authoring.md +30 -0
- package/skills/yarramate-architecture/references/visual-conversations.md +149 -150
- package/dist/adapters/visual/likec4-compiler.d.ts +0 -55
- package/dist/adapters/visual/likec4-compiler.js +0 -460
- package/dist/visual-app/assets/index-CGvC8e3R.css +0 -1
- package/dist/visual-app/assets/index-CXuW1tOj.js +0 -209
package/README.md
CHANGED
|
@@ -184,7 +184,8 @@ people, CI, skills, and agent harnesses.
|
|
|
184
184
|
|
|
185
185
|
Optional adapters provide LikeC4 visualization from semantic projections,
|
|
186
186
|
Graphify observations as evidence overlays, loopback-only visual
|
|
187
|
-
conversations
|
|
187
|
+
conversations that render the native model and land reviewer edits through
|
|
188
|
+
the same validated `apply` batch (beta), and separately governed
|
|
188
189
|
compatibility profiles for external languages. Core depends on none of them.
|
|
189
190
|
|
|
190
191
|
YarraMate is not affiliated with or certified by The Open Group. ArchiMate®
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
format: yarramate/question-catalogue/v1
|
|
2
2
|
id: core-enrichment
|
|
3
|
-
version: "0.
|
|
3
|
+
version: "0.8"
|
|
4
4
|
profile: yarramate/core@0.1
|
|
5
5
|
presentation:
|
|
6
6
|
title: Core enrichment interview
|
|
@@ -1075,3 +1075,54 @@ questions:
|
|
|
1075
1075
|
resolution: >-
|
|
1076
1076
|
Declare architecture states and mark presence with present-in where the
|
|
1077
1077
|
distinction carries decisions; explicitly decline states otherwise.
|
|
1078
|
+
|
|
1079
|
+
- id: kind-untested
|
|
1080
|
+
wave: hygiene
|
|
1081
|
+
since: "0.8"
|
|
1082
|
+
scope: subject
|
|
1083
|
+
subjects:
|
|
1084
|
+
kinds:
|
|
1085
|
+
- yarramate/core@0.1#resource
|
|
1086
|
+
- yarramate/core@0.1#businessActor
|
|
1087
|
+
- yarramate/core@0.1#businessRole
|
|
1088
|
+
- yarramate/core@0.1#businessCollaboration
|
|
1089
|
+
- yarramate/core@0.1#businessInterface
|
|
1090
|
+
- yarramate/core@0.1#applicationComponent
|
|
1091
|
+
- yarramate/core@0.1#applicationCollaboration
|
|
1092
|
+
- yarramate/core@0.1#applicationInterface
|
|
1093
|
+
- yarramate/core@0.1#node
|
|
1094
|
+
- yarramate/core@0.1#device
|
|
1095
|
+
- yarramate/core@0.1#systemSoftware
|
|
1096
|
+
- yarramate/core@0.1#technologyCollaboration
|
|
1097
|
+
- yarramate/core@0.1#technologyInterface
|
|
1098
|
+
- yarramate/core@0.1#path
|
|
1099
|
+
- yarramate/core@0.1#communicationNetwork
|
|
1100
|
+
- yarramate/core@0.1#equipment
|
|
1101
|
+
- yarramate/core@0.1#facility
|
|
1102
|
+
- yarramate/core@0.1#distributionNetwork
|
|
1103
|
+
trigger:
|
|
1104
|
+
- condition: unconstrained-kind
|
|
1105
|
+
question: >-
|
|
1106
|
+
Nothing in the model tests that {subject.name} is a structural element
|
|
1107
|
+
at all: no behaviour is assigned to it, so reclassifying it as a
|
|
1108
|
+
service, an object, or a goal would compile exactly the same. What
|
|
1109
|
+
does it run, host, or perform?
|
|
1110
|
+
askPlain: >-
|
|
1111
|
+
What does {subject.name} actually do, or what runs on it? The model
|
|
1112
|
+
says what it is but nothing says what it takes part in.
|
|
1113
|
+
materiality: >-
|
|
1114
|
+
A structural kind is a promise that something behaves through this
|
|
1115
|
+
element. Only four relationship kinds constrain the aspect of an
|
|
1116
|
+
endpoint, so where none of them touches the subject the kind is a
|
|
1117
|
+
label no check can contradict — the choice between a node, a piece of
|
|
1118
|
+
system software, and an application component stops carrying
|
|
1119
|
+
information, and diagrams inherit a distinction the model never made.
|
|
1120
|
+
authority: either
|
|
1121
|
+
resolution: >-
|
|
1122
|
+
Assign {subject.name} to the behaviour it performs — the process,
|
|
1123
|
+
function, service, or capability it carries. Assignment is the one
|
|
1124
|
+
claim whose source endpoint must be an active structure, so recording
|
|
1125
|
+
it makes the classification falsifiable. If nothing in scope is
|
|
1126
|
+
assigned to it, reclassify it to a kind that does carry a claim, or
|
|
1127
|
+
retire it with a description saying why it sits outside this
|
|
1128
|
+
architecture.
|
|
File without changes
|
|
File without changes
|
package/dist/adapters/mcp-cli.js
CHANGED
|
File without changes
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* contract: it can import these declarations and limits without pulling Ajv,
|
|
9
9
|
* `node:path`, or the schema documents into its bundle.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
import type { CanvasGraph } from "../../graph-projection.js";
|
|
12
|
+
import type { YarramateApplyResult, YarramateOperation } from "../../operations.js";
|
|
13
|
+
import type { ProjectionDefinition, ProjectionQuery } from "../../projection.js";
|
|
14
|
+
export declare const VISUAL_PROTOCOL_VERSION: "yarramate/visual-protocol/v2";
|
|
12
15
|
export declare const VISUAL_LIMITS: {
|
|
13
16
|
readonly messageBytes: number;
|
|
14
17
|
readonly modelBytes: number;
|
|
@@ -17,9 +20,9 @@ export declare const VISUAL_LIMITS: {
|
|
|
17
20
|
readonly reconnectMs: number;
|
|
18
21
|
readonly staleSessionMs: number;
|
|
19
22
|
};
|
|
20
|
-
export type VisualAuthority =
|
|
23
|
+
export type VisualAuthority = "canonical";
|
|
21
24
|
export interface VisualDiagnostic {
|
|
22
|
-
readonly severity:
|
|
25
|
+
readonly severity: "error";
|
|
23
26
|
readonly code: string;
|
|
24
27
|
readonly message: string;
|
|
25
28
|
readonly path: string;
|
|
@@ -28,38 +31,32 @@ export interface VisualDiagnostic {
|
|
|
28
31
|
readonly column: number;
|
|
29
32
|
}
|
|
30
33
|
export interface VisualDiagnosticResult {
|
|
31
|
-
readonly format:
|
|
34
|
+
readonly format: "yarramate/visual-diagnostic-result/v1";
|
|
32
35
|
readonly diagnostics: readonly VisualDiagnostic[];
|
|
33
36
|
}
|
|
34
37
|
export interface VisualCapabilities {
|
|
35
38
|
readonly chat: boolean;
|
|
36
39
|
readonly choices: boolean;
|
|
37
40
|
readonly navigation: boolean;
|
|
38
|
-
readonly modelReplacement: boolean;
|
|
39
41
|
readonly transcript: boolean;
|
|
40
42
|
}
|
|
41
43
|
export interface VisualModel {
|
|
42
|
-
readonly format:
|
|
44
|
+
readonly format: "yarramate/visual-model/v1";
|
|
43
45
|
readonly authority: VisualAuthority;
|
|
44
46
|
readonly initialView: string;
|
|
45
47
|
readonly sourceDigests: Readonly<Record<string, string>>;
|
|
46
|
-
readonly
|
|
47
|
-
}
|
|
48
|
-
export interface VisualCompilerCommand {
|
|
49
|
-
readonly command: string;
|
|
50
|
-
readonly args: readonly string[];
|
|
48
|
+
readonly graph: CanvasGraph;
|
|
51
49
|
}
|
|
52
50
|
export interface VisualSessionRequest {
|
|
53
|
-
readonly format:
|
|
54
|
-
readonly authority: VisualModel[
|
|
51
|
+
readonly format: "yarramate/visual-session-request/v1";
|
|
52
|
+
readonly authority: VisualModel["authority"];
|
|
55
53
|
readonly title: string;
|
|
56
54
|
readonly description: string;
|
|
57
55
|
readonly chatEnabled: boolean;
|
|
58
|
-
readonly compiler: VisualCompilerCommand;
|
|
59
56
|
readonly initialModel: VisualModel;
|
|
60
57
|
}
|
|
61
58
|
export interface VisualSessionStarted {
|
|
62
|
-
readonly format:
|
|
59
|
+
readonly format: "yarramate/visual-session-started/v1";
|
|
63
60
|
readonly protocolVersion: typeof VISUAL_PROTOCOL_VERSION;
|
|
64
61
|
readonly sessionId: string;
|
|
65
62
|
readonly authority: VisualAuthority;
|
|
@@ -74,7 +71,7 @@ export interface VisualSessionStarted {
|
|
|
74
71
|
readonly startedAt: string;
|
|
75
72
|
}
|
|
76
73
|
export interface VisualSessionDescriptor {
|
|
77
|
-
readonly format:
|
|
74
|
+
readonly format: "yarramate/visual-session-descriptor/v1";
|
|
78
75
|
readonly protocolVersion: typeof VISUAL_PROTOCOL_VERSION;
|
|
79
76
|
readonly sessionId: string;
|
|
80
77
|
readonly origin: string;
|
|
@@ -94,6 +91,44 @@ export interface VisualViewNavigatePayload {
|
|
|
94
91
|
readonly viewId: string;
|
|
95
92
|
readonly requiresAttention: boolean;
|
|
96
93
|
}
|
|
94
|
+
export interface VisualViewSummary {
|
|
95
|
+
readonly id: string;
|
|
96
|
+
readonly title: string;
|
|
97
|
+
readonly description: string;
|
|
98
|
+
readonly query: ProjectionQuery;
|
|
99
|
+
readonly presentation: ProjectionDefinition["presentation"];
|
|
100
|
+
}
|
|
101
|
+
export interface VisualKindOption {
|
|
102
|
+
readonly id: string;
|
|
103
|
+
readonly label: string;
|
|
104
|
+
}
|
|
105
|
+
export interface VisualFilterQueryPayload {
|
|
106
|
+
readonly query: ProjectionQuery;
|
|
107
|
+
}
|
|
108
|
+
export interface VisualFilterResultPayload {
|
|
109
|
+
readonly query: ProjectionQuery;
|
|
110
|
+
readonly matchedIds: readonly string[];
|
|
111
|
+
}
|
|
112
|
+
export interface VisualViewSavePayload {
|
|
113
|
+
readonly id?: string;
|
|
114
|
+
readonly title: string;
|
|
115
|
+
readonly description: string;
|
|
116
|
+
readonly query: ProjectionQuery;
|
|
117
|
+
readonly presentation: ProjectionDefinition["presentation"];
|
|
118
|
+
}
|
|
119
|
+
export interface VisualLayoutPositions {
|
|
120
|
+
readonly [subjectId: string]: {
|
|
121
|
+
readonly x: number;
|
|
122
|
+
readonly y: number;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export interface VisualChangesetCommitPayload {
|
|
126
|
+
readonly operations: readonly YarramateOperation[];
|
|
127
|
+
}
|
|
128
|
+
export interface VisualLayoutSavePayload {
|
|
129
|
+
readonly projectionId: string;
|
|
130
|
+
readonly positions: VisualLayoutPositions;
|
|
131
|
+
}
|
|
97
132
|
/**
|
|
98
133
|
* Terminal event payload. Every reason is the runtime's to choose: only it
|
|
99
134
|
* knows whether a session ended by request, by a failing child, by a browser
|
|
@@ -104,7 +139,7 @@ export interface VisualSessionEndPayload {
|
|
|
104
139
|
}
|
|
105
140
|
/** End as an untrusted browser may ask for it, and nothing more. */
|
|
106
141
|
export interface VisualBrowserSessionEndPayload {
|
|
107
|
-
readonly reason:
|
|
142
|
+
readonly reason: "user-ended";
|
|
108
143
|
}
|
|
109
144
|
export interface VisualBrowserConnectedPayload {
|
|
110
145
|
readonly connectionId: string;
|
|
@@ -125,24 +160,40 @@ export interface VisualBrowserDisconnectedPayload {
|
|
|
125
160
|
* sequence and event identifier.
|
|
126
161
|
*/
|
|
127
162
|
export type VisualBrowserInput = {
|
|
128
|
-
readonly type:
|
|
163
|
+
readonly type: "chat.message";
|
|
129
164
|
readonly lastAcknowledgedSequence: number;
|
|
130
165
|
readonly payload: VisualChatMessagePayload;
|
|
131
166
|
} | {
|
|
132
|
-
readonly type:
|
|
167
|
+
readonly type: "choice.selected";
|
|
133
168
|
readonly lastAcknowledgedSequence: number;
|
|
134
169
|
readonly payload: VisualChoiceSelectedPayload;
|
|
135
170
|
} | {
|
|
136
|
-
readonly type:
|
|
171
|
+
readonly type: "view.navigate";
|
|
137
172
|
readonly lastAcknowledgedSequence: number;
|
|
138
173
|
readonly payload: VisualViewNavigatePayload;
|
|
139
174
|
} | {
|
|
140
|
-
readonly type:
|
|
175
|
+
readonly type: "filter.query";
|
|
176
|
+
readonly lastAcknowledgedSequence: number;
|
|
177
|
+
readonly payload: VisualFilterQueryPayload;
|
|
178
|
+
} | {
|
|
179
|
+
readonly type: "view.save";
|
|
180
|
+
readonly lastAcknowledgedSequence: number;
|
|
181
|
+
readonly payload: VisualViewSavePayload;
|
|
182
|
+
} | {
|
|
183
|
+
readonly type: "changeset.commit";
|
|
184
|
+
readonly lastAcknowledgedSequence: number;
|
|
185
|
+
readonly payload: VisualChangesetCommitPayload;
|
|
186
|
+
} | {
|
|
187
|
+
readonly type: "layout.save";
|
|
188
|
+
readonly lastAcknowledgedSequence: number;
|
|
189
|
+
readonly payload: VisualLayoutSavePayload;
|
|
190
|
+
} | {
|
|
191
|
+
readonly type: "session.end";
|
|
141
192
|
readonly lastAcknowledgedSequence: number;
|
|
142
193
|
readonly payload: VisualBrowserSessionEndPayload;
|
|
143
194
|
};
|
|
144
195
|
interface VisualEventEnvelope<Type extends string, Payload> {
|
|
145
|
-
readonly format:
|
|
196
|
+
readonly format: "yarramate/visual-event/v1";
|
|
146
197
|
readonly sessionId: string;
|
|
147
198
|
readonly sequence: number;
|
|
148
199
|
readonly eventId: string;
|
|
@@ -150,12 +201,25 @@ interface VisualEventEnvelope<Type extends string, Payload> {
|
|
|
150
201
|
readonly timestamp: string;
|
|
151
202
|
readonly payload: Payload;
|
|
152
203
|
}
|
|
153
|
-
export type VisualEvent = VisualEventEnvelope<
|
|
204
|
+
export type VisualEvent = VisualEventEnvelope<"chat.message", VisualChatMessagePayload> | VisualEventEnvelope<"choice.selected", VisualChoiceSelectedPayload> | VisualEventEnvelope<"view.navigate", VisualViewNavigatePayload> | VisualEventEnvelope<"session.end", VisualSessionEndPayload> | VisualEventEnvelope<"browser.connected", VisualBrowserConnectedPayload> | VisualEventEnvelope<"browser.disconnected", VisualBrowserDisconnectedPayload> | VisualEventEnvelope<"filter.query", VisualFilterQueryPayload> | VisualEventEnvelope<"view.save", VisualViewSavePayload> | VisualEventEnvelope<"changeset.commit", VisualChangesetCommitPayload> | VisualEventEnvelope<"layout.save", VisualLayoutSavePayload>;
|
|
205
|
+
/**
|
|
206
|
+
* The filter a chat turn resolved to. The agent states `query` and nothing
|
|
207
|
+
* else: the runtime evaluates it against the same graph a `filter.query`
|
|
208
|
+
* event is evaluated against and fills `matchedIds` in before the response
|
|
209
|
+
* is journaled or streamed (ADR 0090). `matchedIds` is optional so a
|
|
210
|
+
* resolved response survives a transcript round-trip - an agent that sends
|
|
211
|
+
* one is refused.
|
|
212
|
+
*/
|
|
213
|
+
export interface VisualChatAppliedQuery {
|
|
214
|
+
readonly query: ProjectionQuery;
|
|
215
|
+
readonly matchedIds?: readonly string[];
|
|
216
|
+
}
|
|
154
217
|
export interface VisualChatResponsePayload {
|
|
155
218
|
readonly text: string;
|
|
219
|
+
readonly appliedQuery?: VisualChatAppliedQuery;
|
|
156
220
|
}
|
|
157
221
|
export interface VisualAgentStatusPayload {
|
|
158
|
-
readonly state:
|
|
222
|
+
readonly state: "thinking" | "compiling" | "waiting" | "idle";
|
|
159
223
|
readonly detail?: string;
|
|
160
224
|
}
|
|
161
225
|
export interface VisualChoiceOption {
|
|
@@ -168,9 +232,6 @@ export interface VisualChoicePresentPayload {
|
|
|
168
232
|
readonly question: string;
|
|
169
233
|
readonly options: readonly VisualChoiceOption[];
|
|
170
234
|
}
|
|
171
|
-
export interface VisualModelReplacePayload {
|
|
172
|
-
readonly model: VisualModel;
|
|
173
|
-
}
|
|
174
235
|
export interface VisualHandoffSummary {
|
|
175
236
|
readonly summary: string;
|
|
176
237
|
readonly confirmedDecisions: readonly string[];
|
|
@@ -181,8 +242,30 @@ export interface VisualHandoffSummary {
|
|
|
181
242
|
export interface VisualDiagnosticPayload {
|
|
182
243
|
readonly diagnostics: readonly VisualDiagnostic[];
|
|
183
244
|
}
|
|
245
|
+
export type VisualViewSaveResultPayload = {
|
|
246
|
+
readonly ok: true;
|
|
247
|
+
readonly id: string;
|
|
248
|
+
readonly path: string;
|
|
249
|
+
} | {
|
|
250
|
+
readonly ok: false;
|
|
251
|
+
readonly diagnostics: readonly VisualDiagnostic[];
|
|
252
|
+
};
|
|
253
|
+
export type VisualApplyResultPayload = {
|
|
254
|
+
readonly ok: true;
|
|
255
|
+
readonly result: YarramateApplyResult;
|
|
256
|
+
} | {
|
|
257
|
+
readonly ok: false;
|
|
258
|
+
readonly diagnostics: readonly VisualDiagnostic[];
|
|
259
|
+
};
|
|
260
|
+
export type VisualLayoutSaveResultPayload = {
|
|
261
|
+
readonly ok: true;
|
|
262
|
+
readonly path: string;
|
|
263
|
+
} | {
|
|
264
|
+
readonly ok: false;
|
|
265
|
+
readonly message: string;
|
|
266
|
+
};
|
|
184
267
|
interface VisualResponseEnvelope<Type extends string, Payload> {
|
|
185
|
-
readonly format:
|
|
268
|
+
readonly format: "yarramate/visual-response/v1";
|
|
186
269
|
readonly sessionId: string;
|
|
187
270
|
readonly responseId: string;
|
|
188
271
|
readonly eventId: string;
|
|
@@ -190,11 +273,11 @@ interface VisualResponseEnvelope<Type extends string, Payload> {
|
|
|
190
273
|
readonly timestamp: string;
|
|
191
274
|
readonly payload: Payload;
|
|
192
275
|
}
|
|
193
|
-
export type VisualResponse = VisualResponseEnvelope<
|
|
194
|
-
export type VisualHandoffDecision =
|
|
195
|
-
export type VisualTerminationReason =
|
|
276
|
+
export type VisualResponse = VisualResponseEnvelope<"chat.response", VisualChatResponsePayload> | VisualResponseEnvelope<"agent.status", VisualAgentStatusPayload> | VisualResponseEnvelope<"choice.present", VisualChoicePresentPayload> | VisualResponseEnvelope<"handoff.complete", VisualHandoffSummary> | VisualResponseEnvelope<"diagnostic", VisualDiagnosticPayload>;
|
|
277
|
+
export type VisualHandoffDecision = "completed" | "cancelled" | "failed";
|
|
278
|
+
export type VisualTerminationReason = "user-ended" | "child-failed" | "browser-timeout" | "main-cancelled" | "server-failed" | "compiler-failed";
|
|
196
279
|
export interface VisualHandoff extends VisualHandoffSummary {
|
|
197
|
-
readonly format:
|
|
280
|
+
readonly format: "yarramate/visual-handoff/v1";
|
|
198
281
|
readonly sessionId: string;
|
|
199
282
|
readonly authority: VisualAuthority;
|
|
200
283
|
readonly decision: VisualHandoffDecision;
|
|
@@ -204,10 +287,10 @@ export interface VisualHandoff extends VisualHandoffSummary {
|
|
|
204
287
|
readonly transcript?: readonly (VisualEvent | VisualResponse)[];
|
|
205
288
|
readonly completedAt: string;
|
|
206
289
|
}
|
|
207
|
-
export type VisualLifecycle =
|
|
208
|
-
export type VisualFreezeReason =
|
|
290
|
+
export type VisualLifecycle = "starting" | "running" | "draining" | "stopped";
|
|
291
|
+
export type VisualFreezeReason = "message-bytes" | "model-bytes" | "transcript-bytes" | "pending-events" | "browser-disconnected" | "terminal-event" | "recompile-failed";
|
|
209
292
|
export interface VisualStatus {
|
|
210
|
-
readonly format:
|
|
293
|
+
readonly format: "yarramate/visual-status/v1";
|
|
211
294
|
readonly protocolVersion: typeof VISUAL_PROTOCOL_VERSION;
|
|
212
295
|
readonly sessionId: string;
|
|
213
296
|
readonly lifecycle: VisualLifecycle;
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Every visual protocol declaration, and the limits they are measured against,
|
|
3
|
-
* with no runtime dependency of any kind.
|
|
4
|
-
*
|
|
5
|
-
* The validators that police these documents live in `./protocol.js`, which
|
|
6
|
-
* re-exports this module so a Node consumer still has one import for the whole
|
|
7
|
-
* protocol. The split exists because the browser application ships the same
|
|
8
|
-
* contract: it can import these declarations and limits without pulling Ajv,
|
|
9
|
-
* `node:path`, or the schema documents into its bundle.
|
|
10
|
-
*/
|
|
11
|
-
export const VISUAL_PROTOCOL_VERSION = 'yarramate/visual-protocol/v1';
|
|
1
|
+
export const VISUAL_PROTOCOL_VERSION = "yarramate/visual-protocol/v2";
|
|
12
2
|
export const VISUAL_LIMITS = {
|
|
13
3
|
messageBytes: 64 * 1024,
|
|
14
4
|
modelBytes: 5 * 1024 * 1024,
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { type ParseResult, type VisualBrowserInput, type VisualDiagnosticResult, type VisualEvent, type VisualHandoff, type VisualModel, type VisualResponse, type VisualSessionDescriptor, type VisualSessionRequest, type VisualSessionStarted, type VisualStatus } from './protocol-contract.js';
|
|
2
2
|
export * from './protocol-contract.js';
|
|
3
|
-
export declare const isSafeVisualModelPath: (candidate: string) => boolean;
|
|
4
3
|
export declare const parseVisualModel: (input: unknown) => ParseResult<VisualModel>;
|
|
5
4
|
export declare const parseVisualSessionRequest: (input: unknown) => ParseResult<VisualSessionRequest>;
|
|
6
5
|
export declare const parseVisualSessionStarted: (input: unknown) => ParseResult<VisualSessionStarted>;
|
|
7
6
|
export declare const parseVisualSessionDescriptor: (input: unknown) => ParseResult<VisualSessionDescriptor>;
|
|
7
|
+
/**
|
|
8
|
+
* The type an untrusted frame claims to be, when the protocol has that type.
|
|
9
|
+
*
|
|
10
|
+
* A refusal has to say what it refused, and the browser is holding a control
|
|
11
|
+
* open until it does. The claim is worth reporting before the document is
|
|
12
|
+
* known to be valid because the frame that named it is the one that failed.
|
|
13
|
+
*/
|
|
14
|
+
export declare const visualBrowserInputType: (input: unknown) => VisualBrowserInput['type'] | undefined;
|
|
8
15
|
export declare const parseVisualBrowserInput: (input: unknown) => ParseResult<VisualBrowserInput>;
|
|
9
16
|
export declare const parseVisualEvent: (input: unknown) => ParseResult<VisualEvent>;
|
|
10
17
|
export declare const parseVisualResponse: (input: unknown) => ParseResult<VisualResponse>;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { posix } from 'node:path';
|
|
2
2
|
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
3
|
-
import { describeSchemaViolation } from '../../source-document.js';
|
|
3
|
+
import { describeSchemaViolation, readableSchemaErrors, } from '../../source-document.js';
|
|
4
4
|
import visualDiagnosticResultSchema from '../../../schema/yarramate-visual-diagnostic-result.schema.json' with { type: 'json'
|
|
5
5
|
};
|
|
6
6
|
import visualEventSchema from '../../../schema/yarramate-visual-event.schema.json' with { type: 'json'
|
|
7
7
|
};
|
|
8
|
+
import visualGraphSchema from '../../../schema/yarramate-visual-graph.schema.json' with { type: 'json'
|
|
9
|
+
};
|
|
10
|
+
import applyResultSchema from '../../../schema/yarramate-apply-result.schema.json' with { type: 'json'
|
|
11
|
+
};
|
|
12
|
+
import operationsSchema from '../../../schema/yarramate-operations.schema.json' with { type: 'json'
|
|
13
|
+
};
|
|
14
|
+
import projectionSchema from '../../../schema/yarramate-projection.schema.json' with { type: 'json'
|
|
15
|
+
};
|
|
8
16
|
import visualHandoffSchema from '../../../schema/yarramate-visual-handoff.schema.json' with { type: 'json'
|
|
9
17
|
};
|
|
18
|
+
import visualLayoutSchema from '../../../schema/yarramate-visual-layout.schema.json' with { type: 'json'
|
|
19
|
+
};
|
|
10
20
|
import visualModelSchema from '../../../schema/yarramate-visual-model.schema.json' with { type: 'json'
|
|
11
21
|
};
|
|
12
22
|
import visualResponseSchema from '../../../schema/yarramate-visual-response.schema.json' with { type: 'json'
|
|
@@ -24,17 +34,28 @@ import { VISUAL_LIMITS, } from './protocol-contract.js';
|
|
|
24
34
|
// validates travel with the validators.
|
|
25
35
|
export * from './protocol-contract.js';
|
|
26
36
|
const Ajv2020 = Ajv2020Module.default;
|
|
27
|
-
|
|
37
|
+
// `discriminator` on Core's operation union routes a staged edit to the branch
|
|
38
|
+
// its `op` names, so a browser changeset reports the fault the reviewer made
|
|
39
|
+
// rather than one near-miss per operation kind.
|
|
40
|
+
const ajv = new Ajv2020({ allErrors: true, discriminator: true });
|
|
28
41
|
ajv.addSchema([
|
|
29
42
|
visualDiagnosticResultSchema,
|
|
30
|
-
visualModelSchema,
|
|
31
43
|
visualEventSchema,
|
|
44
|
+
visualGraphSchema,
|
|
45
|
+
visualHandoffSchema,
|
|
46
|
+
visualModelSchema,
|
|
32
47
|
visualResponseSchema,
|
|
48
|
+
visualSessionDescriptorSchema,
|
|
33
49
|
visualSessionRequestSchema,
|
|
34
50
|
visualSessionStartedSchema,
|
|
35
|
-
visualSessionDescriptorSchema,
|
|
36
|
-
visualHandoffSchema,
|
|
37
51
|
visualStatusSchema,
|
|
52
|
+
visualLayoutSchema,
|
|
53
|
+
projectionSchema,
|
|
54
|
+
// The commit path's event and response documents reference Core's operations
|
|
55
|
+
// and apply-result shapes rather than restating them, so Ajv needs both
|
|
56
|
+
// resolvable before it compiles the visual validators.
|
|
57
|
+
operationsSchema,
|
|
58
|
+
applyResultSchema,
|
|
38
59
|
]);
|
|
39
60
|
// Diagnostics report the document they came from rather than a source file,
|
|
40
61
|
// because visual protocol documents arrive as parsed JSON over the wire.
|
|
@@ -50,7 +71,22 @@ const validateVisualModel = visualValidator('visual-model/v1');
|
|
|
50
71
|
const validateVisualSessionRequest = visualValidator('visual-session-request/v1');
|
|
51
72
|
const validateVisualSessionStarted = visualValidator('visual-session-started/v1');
|
|
52
73
|
const validateVisualSessionDescriptor = visualValidator('visual-session-descriptor/v1');
|
|
53
|
-
const
|
|
74
|
+
const BROWSER_INPUT_REFERENCE = 'https://yarramate.org/schema/visual-event/v1#/$defs/browserInput';
|
|
75
|
+
const validateVisualBrowserInput = visualValidator('visual-browser-input/v1', BROWSER_INPUT_REFERENCE);
|
|
76
|
+
/**
|
|
77
|
+
* One validator per input type, keyed by the `type` its branch fixes.
|
|
78
|
+
*
|
|
79
|
+
* The union reports every branch it tried, so a `changeset.commit` that gets
|
|
80
|
+
* one field wrong arrives as that one violation buried under seven other
|
|
81
|
+
* types' missing properties - unreadable, and it is the reviewer who has to
|
|
82
|
+
* read it. An input that names a type the protocol knows is answered against
|
|
83
|
+
* that type alone. The vocabulary is read off the schema rather than restated
|
|
84
|
+
* here, so a new input type cannot be added without one.
|
|
85
|
+
*/
|
|
86
|
+
const browserInputBranches = new Map(visualEventSchema.$defs.browserInput.oneOf.map((branch, index) => [
|
|
87
|
+
branch.properties.type.const,
|
|
88
|
+
visualValidator('visual-browser-input/v1', `${BROWSER_INPUT_REFERENCE}/oneOf/${index}`),
|
|
89
|
+
]));
|
|
54
90
|
const validateVisualEvent = visualValidator('visual-event/v1');
|
|
55
91
|
const validateVisualResponse = visualValidator('visual-response/v1');
|
|
56
92
|
const validateVisualHandoff = visualValidator('visual-handoff/v1');
|
|
@@ -83,13 +119,12 @@ const jsonBytes = (value) => {
|
|
|
83
119
|
return 0;
|
|
84
120
|
return Buffer.byteLength(encoded, 'utf8');
|
|
85
121
|
};
|
|
86
|
-
const MODEL_ROOT_CONFIG = 'likec4.config.json';
|
|
87
|
-
const MODEL_SOURCE_EXTENSIONS = ['.c4', '.likec4'];
|
|
88
122
|
/**
|
|
89
123
|
* Relative POSIX path confined to the candidate root. Absolute paths, parent
|
|
90
|
-
* traversal, empty segments,
|
|
91
|
-
*
|
|
92
|
-
*
|
|
124
|
+
* traversal, empty segments, `.`/`..` segments, Windows separators, and NUL
|
|
125
|
+
* bytes are all rejected. A dot-prefixed directory is not an escape and is
|
|
126
|
+
* kept: every workspace keeps its documents under `.yarramate/`, so the
|
|
127
|
+
* digests of a canonical model are unrepresentable without it.
|
|
93
128
|
*/
|
|
94
129
|
const isConfinedRelativePath = (candidate) => {
|
|
95
130
|
if (candidate.length === 0 || candidate.length > 1024)
|
|
@@ -102,35 +137,11 @@ const isConfinedRelativePath = (candidate) => {
|
|
|
102
137
|
return false;
|
|
103
138
|
return candidate
|
|
104
139
|
.split('/')
|
|
105
|
-
.every((segment) => segment.length > 0 &&
|
|
106
|
-
};
|
|
107
|
-
export const isSafeVisualModelPath = (candidate) => {
|
|
108
|
-
if (!isConfinedRelativePath(candidate))
|
|
109
|
-
return false;
|
|
110
|
-
if (candidate === MODEL_ROOT_CONFIG)
|
|
111
|
-
return true;
|
|
112
|
-
const extension = posix.extname(candidate);
|
|
113
|
-
return MODEL_SOURCE_EXTENSIONS.some((allowed) => allowed === extension);
|
|
140
|
+
.every((segment) => segment.length > 0 && segment !== '.' && segment !== '..');
|
|
114
141
|
};
|
|
115
142
|
const modelSemantics = (input, path, prefix) => {
|
|
116
143
|
const fields = documentFields(input);
|
|
117
144
|
const diagnostics = [];
|
|
118
|
-
const files = documentFields(fields.files);
|
|
119
|
-
const fileKeys = Object.keys(files);
|
|
120
|
-
if (fileKeys.length > 0) {
|
|
121
|
-
let sources = 0;
|
|
122
|
-
for (const key of fileKeys) {
|
|
123
|
-
if (!isSafeVisualModelPath(key)) {
|
|
124
|
-
diagnostics.push(visualDiagnostic('YMVS113', `Model file "${key}" escapes the candidate root or is not a LikeC4 source`, path, `${prefix}/files/${pointerSegment(key)}`));
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (key !== MODEL_ROOT_CONFIG)
|
|
128
|
-
sources += 1;
|
|
129
|
-
}
|
|
130
|
-
if (sources === 0) {
|
|
131
|
-
diagnostics.push(visualDiagnostic('YMVS114', 'A visual model requires at least one .c4 or .likec4 source file', path, `${prefix}/files`));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
145
|
const digestKeys = Object.keys(documentFields(fields.sourceDigests));
|
|
135
146
|
for (const key of digestKeys) {
|
|
136
147
|
if (isConfinedRelativePath(key))
|
|
@@ -140,9 +151,6 @@ const modelSemantics = (input, path, prefix) => {
|
|
|
140
151
|
if (fields.authority === 'canonical' && digestKeys.length === 0) {
|
|
141
152
|
diagnostics.push(visualDiagnostic('YMVS112', 'A canonical visual model must record the source digests it was derived from', path, `${prefix}/sourceDigests`));
|
|
142
153
|
}
|
|
143
|
-
if (fields.authority === 'ad-hoc' && digestKeys.length > 0) {
|
|
144
|
-
diagnostics.push(visualDiagnostic('YMVS112', 'An ad-hoc visual model must not claim canonical source digests', path, `${prefix}/sourceDigests`));
|
|
145
|
-
}
|
|
146
154
|
if (jsonBytes(input) > VISUAL_LIMITS.modelBytes) {
|
|
147
155
|
diagnostics.push(visualDiagnostic('YMVS115', `A visual model must not exceed ${VISUAL_LIMITS.modelBytes} bytes`, path, prefix === '' ? '/' : prefix));
|
|
148
156
|
}
|
|
@@ -182,9 +190,6 @@ const responseSemantics = (input, path) => {
|
|
|
182
190
|
if (fields.type === 'chat.response') {
|
|
183
191
|
return chatTextSemantics(fields.payload, path, '/payload');
|
|
184
192
|
}
|
|
185
|
-
if (fields.type === 'model.replace') {
|
|
186
|
-
return modelSemantics(documentFields(fields.payload).model, path, '/payload/model');
|
|
187
|
-
}
|
|
188
193
|
return [];
|
|
189
194
|
};
|
|
190
195
|
/**
|
|
@@ -233,15 +238,30 @@ const schemaDiagnostics = (errors, code, path) => errors.map((error) => {
|
|
|
233
238
|
? `Property "${property}" is not allowed`
|
|
234
239
|
: `${path} schema violation: ${describeSchemaViolation(error)}`, path, pointer);
|
|
235
240
|
});
|
|
241
|
+
/**
|
|
242
|
+
* The same violation, reported once.
|
|
243
|
+
*
|
|
244
|
+
* A union reports the branches it tried, and sibling branches that share a
|
|
245
|
+
* requirement each raise it - the reviewer reads one document, not the
|
|
246
|
+
* validator's search. Diagnostics are already sorted, so identical neighbours
|
|
247
|
+
* are adjacent; nothing that differs in code, pointer, or message is lost.
|
|
248
|
+
*/
|
|
249
|
+
const distinct = (diagnostics) => diagnostics.filter((diagnostic, index) => {
|
|
250
|
+
if (index === 0)
|
|
251
|
+
return true;
|
|
252
|
+
const previous = diagnostics[index - 1];
|
|
253
|
+
return (previous === undefined ||
|
|
254
|
+
visualDiagnosticOrder(previous, diagnostic) !== 0);
|
|
255
|
+
});
|
|
236
256
|
const parseWith = (validate, input, code) => {
|
|
237
257
|
const path = documentPaths.get(validate) ?? 'visual-document';
|
|
238
258
|
const semantics = semanticsByDocument[path];
|
|
239
|
-
const diagnostics = [
|
|
259
|
+
const diagnostics = distinct([
|
|
240
260
|
...(validate(input)
|
|
241
261
|
? []
|
|
242
|
-
: schemaDiagnostics(validate.errors ?? [], code, path)),
|
|
262
|
+
: schemaDiagnostics(readableSchemaErrors(validate.errors ?? []), code, path)),
|
|
243
263
|
...(semantics ? semantics(input, path) : []),
|
|
244
|
-
].sort(visualDiagnosticOrder);
|
|
264
|
+
].sort(visualDiagnosticOrder));
|
|
245
265
|
if (diagnostics.length > 0)
|
|
246
266
|
return { ok: false, diagnostics };
|
|
247
267
|
return { ok: true, value: input };
|
|
@@ -250,7 +270,27 @@ export const parseVisualModel = (input) => parseWith(validateVisualModel, input,
|
|
|
250
270
|
export const parseVisualSessionRequest = (input) => parseWith(validateVisualSessionRequest, input, 'YMVS101');
|
|
251
271
|
export const parseVisualSessionStarted = (input) => parseWith(validateVisualSessionStarted, input, 'YMVS102');
|
|
252
272
|
export const parseVisualSessionDescriptor = (input) => parseWith(validateVisualSessionDescriptor, input, 'YMVS103');
|
|
253
|
-
|
|
273
|
+
/**
|
|
274
|
+
* The type an untrusted frame claims to be, when the protocol has that type.
|
|
275
|
+
*
|
|
276
|
+
* A refusal has to say what it refused, and the browser is holding a control
|
|
277
|
+
* open until it does. The claim is worth reporting before the document is
|
|
278
|
+
* known to be valid because the frame that named it is the one that failed.
|
|
279
|
+
*/
|
|
280
|
+
export const visualBrowserInputType = (input) => {
|
|
281
|
+
const claimed = documentFields(input).type;
|
|
282
|
+
return typeof claimed === 'string' && browserInputBranches.has(claimed)
|
|
283
|
+
? claimed
|
|
284
|
+
: undefined;
|
|
285
|
+
};
|
|
286
|
+
export const parseVisualBrowserInput = (input) => {
|
|
287
|
+
const claimed = visualBrowserInputType(input);
|
|
288
|
+
const validate = claimed === undefined
|
|
289
|
+
? validateVisualBrowserInput
|
|
290
|
+
: // The branch is in the map because `claimed` came out of it.
|
|
291
|
+
browserInputBranches.get(claimed);
|
|
292
|
+
return parseWith(validate, input, 'YMVS109');
|
|
293
|
+
};
|
|
254
294
|
export const parseVisualEvent = (input) => parseWith(validateVisualEvent, input, 'YMVS104');
|
|
255
295
|
export const parseVisualResponse = (input) => parseWith(validateVisualResponse, input, 'YMVS105');
|
|
256
296
|
export const parseVisualHandoff = (input) => parseWith(validateVisualHandoff, input, 'YMVS107');
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { VisualDiagnostic, VisualSessionRequest } from './protocol-contract.js';
|
|
2
|
+
export interface VisualSessionRequestOptions {
|
|
3
|
+
readonly cwd: string;
|
|
4
|
+
/** Defaults to the workspace id. */
|
|
5
|
+
readonly title?: string;
|
|
6
|
+
/** Defaults to a line naming the workspace and its manifest. */
|
|
7
|
+
readonly description?: string;
|
|
8
|
+
/** Defaults to the first projection the workspace declares. */
|
|
9
|
+
readonly initialView?: string;
|
|
10
|
+
/** Defaults to `false`: a diagram-only session needs no model provider. */
|
|
11
|
+
readonly chatEnabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export type VisualSessionRequestResult = {
|
|
14
|
+
readonly ok: true;
|
|
15
|
+
readonly request: VisualSessionRequest;
|
|
16
|
+
} | {
|
|
17
|
+
readonly ok: false;
|
|
18
|
+
readonly diagnostics: readonly VisualDiagnostic[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Builds the `yarramate/visual-session-request/v1` document that `start`
|
|
22
|
+
* consumes, from the workspace on disk.
|
|
23
|
+
*
|
|
24
|
+
* The request carries a whole compiled model — 247 nodes and 338 edges for this
|
|
25
|
+
* repository's own architecture — so it is a machine's transcription of the
|
|
26
|
+
* workspace, never something an agent can honestly hand-author. Every input the
|
|
27
|
+
* document needs is already derivable: the graph from the native compiler, the
|
|
28
|
+
* digests from the same sources that compiled, and the view list from the
|
|
29
|
+
* workspace's declared projections.
|
|
30
|
+
*
|
|
31
|
+
* The result is validated through `parseVisualSessionRequest` before it is
|
|
32
|
+
* returned, so the byte ceiling, digest confinement, and authority agreement
|
|
33
|
+
* are the protocol's own rules rather than a second set restated here. A
|
|
34
|
+
* builder that cannot produce a valid document refuses instead of handing a
|
|
35
|
+
* session something it would reject at start.
|
|
36
|
+
*/
|
|
37
|
+
export declare const buildVisualSessionRequest: (options: VisualSessionRequestOptions) => VisualSessionRequestResult;
|