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
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type VisualAuthority, type VisualDiagnostic, type VisualEvent, type VisualFreezeReason, type VisualHandoff, type
|
|
1
|
+
import { type VisualAuthority, type VisualDiagnostic, type VisualEvent, type VisualFreezeReason, type VisualHandoff, type VisualResponse, type VisualSessionDescriptor, type VisualSessionRequest, type VisualTerminationReason } from './protocol.js';
|
|
2
|
+
import { type WorkspaceSource } from '../../compiler.js';
|
|
3
|
+
import { type CanvasGraph } from '../../graph-projection.js';
|
|
2
4
|
export declare const VISUAL_SESSION_MARKER_FORMAT: 'yarramate/visual-session-marker/v1';
|
|
3
|
-
export declare const VISUAL_ACTIVE_MODEL_FORMAT: 'yarramate/visual-active-model/v1';
|
|
4
5
|
/**
|
|
5
6
|
* Upper bound on how many orphaned sessions one `start` prunes. Cleanup runs on
|
|
6
7
|
* the critical path of a new session, so a directory full of orphans must cost
|
|
@@ -19,8 +20,6 @@ export interface VisualSessionPaths {
|
|
|
19
20
|
readonly marker: string;
|
|
20
21
|
readonly descriptor: string;
|
|
21
22
|
readonly journal: string;
|
|
22
|
-
readonly candidates: string;
|
|
23
|
-
readonly activeModel: string;
|
|
24
23
|
}
|
|
25
24
|
export interface SessionDependencies {
|
|
26
25
|
readonly baseDir: string;
|
|
@@ -45,17 +44,6 @@ export interface VisualSessionMarker {
|
|
|
45
44
|
readonly createdAt: string;
|
|
46
45
|
readonly authority: VisualAuthority;
|
|
47
46
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Last-good rendering pointer. It names a staged candidate directory instead of
|
|
50
|
-
* embedding the model so the swap is a single rename of a small file.
|
|
51
|
-
*/
|
|
52
|
-
export interface VisualActiveModel {
|
|
53
|
-
readonly format: typeof VISUAL_ACTIVE_MODEL_FORMAT;
|
|
54
|
-
readonly candidate: string;
|
|
55
|
-
readonly authority: VisualAuthority;
|
|
56
|
-
readonly initialView: string;
|
|
57
|
-
readonly promotedAt: string;
|
|
58
|
-
}
|
|
59
47
|
export interface VisualSessionCreated {
|
|
60
48
|
readonly paths: VisualSessionPaths;
|
|
61
49
|
readonly browserToken: string;
|
|
@@ -73,17 +61,6 @@ export interface VisualAppendRejected {
|
|
|
73
61
|
readonly diagnostics: readonly VisualDiagnostic[];
|
|
74
62
|
}
|
|
75
63
|
export type VisualAppendResult = VisualAppendAccepted | VisualAppendRejected;
|
|
76
|
-
export interface ModelPromotion {
|
|
77
|
-
readonly model: VisualModel;
|
|
78
|
-
readonly now: () => Date;
|
|
79
|
-
readonly compile: (candidateDir: string) => Promise<readonly VisualDiagnostic[]>;
|
|
80
|
-
}
|
|
81
|
-
export interface ModelPromotionResult {
|
|
82
|
-
readonly promoted: boolean;
|
|
83
|
-
readonly candidate: string;
|
|
84
|
-
readonly candidateDir: string;
|
|
85
|
-
readonly diagnostics: readonly VisualDiagnostic[];
|
|
86
|
-
}
|
|
87
64
|
export declare const visualSessionPaths: (root: string) => VisualSessionPaths;
|
|
88
65
|
export declare const createVisualSession: (request: VisualSessionRequest, deps: SessionDependencies) => Promise<VisualSessionCreated>;
|
|
89
66
|
/**
|
|
@@ -105,8 +82,31 @@ export declare const appendVisualResponse: (paths: VisualSessionPaths, response:
|
|
|
105
82
|
export declare const appendTerminalEvent: (paths: VisualSessionPaths, reason: VisualTerminationReason, deps: TerminalEventDependencies) => Promise<VisualEvent>;
|
|
106
83
|
export declare const readActionableEventsAfter: (paths: VisualSessionPaths, sequence: number) => Promise<readonly VisualEvent[]>;
|
|
107
84
|
export declare const recoverVisualSession: (paths: VisualSessionPaths, includeTranscript?: boolean) => Promise<VisualHandoff>;
|
|
108
|
-
export
|
|
109
|
-
|
|
85
|
+
export type VisualModelGraphResult = {
|
|
86
|
+
readonly ok: true;
|
|
87
|
+
readonly graph: CanvasGraph;
|
|
88
|
+
} | {
|
|
89
|
+
readonly ok: false;
|
|
90
|
+
readonly diagnostics: readonly VisualDiagnostic[];
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Builds the `graph` a session's `VisualModel` renders, from the workspace's
|
|
94
|
+
* own source documents. This is the whole construction step a visual session
|
|
95
|
+
* needs at start: no subprocess, no staged candidate directory, no on-disk
|
|
96
|
+
* pointer file — `compileWorkspaceWithProfileContext` and
|
|
97
|
+
* `projectGraphForCanvas` are both pure, synchronous functions over this
|
|
98
|
+
* repo's native compiler, so the caller assembles the result directly into
|
|
99
|
+
* the full `VisualModel` (format, authority, initialView, sourceDigests,
|
|
100
|
+
* graph) without any session-store I/O.
|
|
101
|
+
*
|
|
102
|
+
* A compile failure here is not a runtime defect — it is exactly the
|
|
103
|
+
* diagnostic-shaped rejection the browser already expects, so the workspace
|
|
104
|
+
* compiler's own diagnostics are returned unchanged rather than resynthesised.
|
|
105
|
+
* `Diagnostic` (compiler.ts) and `VisualDiagnostic` (protocol-contract.ts)
|
|
106
|
+
* already share one shape: severity, code, message, path, pointer, line,
|
|
107
|
+
* column.
|
|
108
|
+
*/
|
|
109
|
+
export declare const buildVisualModelGraph: (sources: readonly WorkspaceSource[]) => VisualModelGraphResult;
|
|
110
110
|
/**
|
|
111
111
|
* Recovers the handoff and only then deletes the session, so cleanup can never
|
|
112
112
|
* be the step that loses confirmed state. Returns `undefined` when the session
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { constants } from 'node:fs';
|
|
2
2
|
import { lstat, mkdir, open, readFile, readdir, rename, rm, stat, truncate, writeFile, } from 'node:fs/promises';
|
|
3
|
-
import { basename, dirname, join, resolve
|
|
4
|
-
import { VISUAL_LIMITS, parseVisualEvent, parseVisualHandoff,
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { VISUAL_LIMITS, parseVisualEvent, parseVisualHandoff, parseVisualResponse, parseVisualSessionDescriptor, parseVisualSessionRequest, } from './protocol.js';
|
|
5
|
+
import { compileWorkspaceWithProfileContext, } from '../../compiler.js';
|
|
6
|
+
import { projectGraphForCanvas } from '../../graph-projection.js';
|
|
5
7
|
export const VISUAL_SESSION_MARKER_FORMAT = 'yarramate/visual-session-marker/v1';
|
|
6
|
-
export const VISUAL_ACTIVE_MODEL_FORMAT = 'yarramate/visual-active-model/v1';
|
|
7
8
|
/**
|
|
8
9
|
* Upper bound on how many orphaned sessions one `start` prunes. Cleanup runs on
|
|
9
10
|
* the critical path of a new session, so a directory full of orphans must cost
|
|
10
11
|
* a bounded amount of work; the remainder is collected by the next start.
|
|
11
12
|
*/
|
|
12
13
|
export const VISUAL_SESSION_PRUNE_LIMIT = 64;
|
|
13
|
-
const CANDIDATE_NAME = /^[0-9]{6}$/;
|
|
14
14
|
const IDENTIFIER = /^[0-9a-f]{32}$/;
|
|
15
15
|
const TIMESTAMP = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/;
|
|
16
16
|
const NEWLINE = 0x0a;
|
|
@@ -58,7 +58,6 @@ const serialize = (key, action) => {
|
|
|
58
58
|
const forget = (paths) => {
|
|
59
59
|
states.delete(paths.journal);
|
|
60
60
|
queues.delete(paths.journal);
|
|
61
|
-
queues.delete(paths.candidates);
|
|
62
61
|
};
|
|
63
62
|
const drawHex = (randomBytes, size) => {
|
|
64
63
|
const drawn = randomBytes(size);
|
|
@@ -113,8 +112,6 @@ export const visualSessionPaths = (root) => {
|
|
|
113
112
|
marker: join(base, 'session.json'),
|
|
114
113
|
descriptor: join(base, 'descriptor.json'),
|
|
115
114
|
journal: join(base, 'journal.jsonl'),
|
|
116
|
-
candidates: join(base, 'candidates'),
|
|
117
|
-
activeModel: join(base, 'active-model.json'),
|
|
118
115
|
};
|
|
119
116
|
};
|
|
120
117
|
const readSessionMarker = async (paths) => {
|
|
@@ -141,7 +138,7 @@ const readSessionMarker = async (paths) => {
|
|
|
141
138
|
!IDENTIFIER.test(id) ||
|
|
142
139
|
typeof createdAt !== 'string' ||
|
|
143
140
|
!TIMESTAMP.test(createdAt) ||
|
|
144
|
-
|
|
141
|
+
authority !== 'canonical') {
|
|
145
142
|
throw storeError('YMVS124', `Session marker "${paths.marker}" is not ${VISUAL_SESSION_MARKER_FORMAT}`);
|
|
146
143
|
}
|
|
147
144
|
// A marker that names another directory cannot authorise destructive work on
|
|
@@ -277,7 +274,6 @@ export const createVisualSession = async (request, deps) => {
|
|
|
277
274
|
createdAt: deps.now().toISOString(),
|
|
278
275
|
authority: validated.value.authority,
|
|
279
276
|
});
|
|
280
|
-
await mkdir(paths.candidates, { recursive: false, mode: 0o700 });
|
|
281
277
|
await writeFile(paths.journal, '', { mode: 0o600 });
|
|
282
278
|
await syncDirectory(root);
|
|
283
279
|
return {
|
|
@@ -559,87 +555,33 @@ export const recoverVisualSession = async (paths, includeTranscript = false) =>
|
|
|
559
555
|
}
|
|
560
556
|
return handoff;
|
|
561
557
|
};
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
typeof candidate !== 'string' ||
|
|
584
|
-
!CANDIDATE_NAME.test(candidate) ||
|
|
585
|
-
(authority !== 'canonical' && authority !== 'ad-hoc') ||
|
|
586
|
-
typeof initialView !== 'string' ||
|
|
587
|
-
typeof promotedAt !== 'string' ||
|
|
588
|
-
!TIMESTAMP.test(promotedAt)) {
|
|
589
|
-
throw storeError('YMVS128', `Active model pointer "${paths.activeModel}" is not ${VISUAL_ACTIVE_MODEL_FORMAT}`);
|
|
558
|
+
/**
|
|
559
|
+
* Builds the `graph` a session's `VisualModel` renders, from the workspace's
|
|
560
|
+
* own source documents. This is the whole construction step a visual session
|
|
561
|
+
* needs at start: no subprocess, no staged candidate directory, no on-disk
|
|
562
|
+
* pointer file — `compileWorkspaceWithProfileContext` and
|
|
563
|
+
* `projectGraphForCanvas` are both pure, synchronous functions over this
|
|
564
|
+
* repo's native compiler, so the caller assembles the result directly into
|
|
565
|
+
* the full `VisualModel` (format, authority, initialView, sourceDigests,
|
|
566
|
+
* graph) without any session-store I/O.
|
|
567
|
+
*
|
|
568
|
+
* A compile failure here is not a runtime defect — it is exactly the
|
|
569
|
+
* diagnostic-shaped rejection the browser already expects, so the workspace
|
|
570
|
+
* compiler's own diagnostics are returned unchanged rather than resynthesised.
|
|
571
|
+
* `Diagnostic` (compiler.ts) and `VisualDiagnostic` (protocol-contract.ts)
|
|
572
|
+
* already share one shape: severity, code, message, path, pointer, line,
|
|
573
|
+
* column.
|
|
574
|
+
*/
|
|
575
|
+
export const buildVisualModelGraph = (sources) => {
|
|
576
|
+
const compiled = compileWorkspaceWithProfileContext(sources);
|
|
577
|
+
if (!compiled.ok) {
|
|
578
|
+
return { ok: false, diagnostics: compiled.diagnostics };
|
|
590
579
|
}
|
|
591
580
|
return {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
authority,
|
|
595
|
-
initialView,
|
|
596
|
-
promotedAt,
|
|
581
|
+
ok: true,
|
|
582
|
+
graph: projectGraphForCanvas(compiled.graph, compiled.profileContext),
|
|
597
583
|
};
|
|
598
584
|
};
|
|
599
|
-
export const promoteCompiledModel = async (paths, promotion) => {
|
|
600
|
-
// The runtime has already parsed the `model.replace` response that carried
|
|
601
|
-
// this model, so a violation here is a runtime defect rather than untrusted
|
|
602
|
-
// input, and must not be reported to the browser as a candidate diagnostic.
|
|
603
|
-
const validated = parseVisualModel(promotion.model);
|
|
604
|
-
if (!validated.ok) {
|
|
605
|
-
const first = validated.diagnostics[0];
|
|
606
|
-
throw storeError(first?.code ?? 'YMVS106', `Candidate model is invalid: ${first?.message ?? 'unknown violation'}`);
|
|
607
|
-
}
|
|
608
|
-
const model = validated.value;
|
|
609
|
-
return serialize(paths.candidates, async () => {
|
|
610
|
-
await readSessionMarker(paths);
|
|
611
|
-
const existing = await readdir(paths.candidates);
|
|
612
|
-
const highest = existing
|
|
613
|
-
.filter((entry) => CANDIDATE_NAME.test(entry))
|
|
614
|
-
.reduce((max, entry) => Math.max(max, Number(entry)), 0);
|
|
615
|
-
const candidate = String(highest + 1).padStart(6, '0');
|
|
616
|
-
const candidateDir = join(paths.candidates, candidate);
|
|
617
|
-
await mkdir(candidateDir, { recursive: false, mode: 0o700 });
|
|
618
|
-
for (const [file, contents] of Object.entries(model.files)) {
|
|
619
|
-
const target = resolve(candidateDir, file);
|
|
620
|
-
// Confinement is already enforced by the model schema and by the protocol
|
|
621
|
-
// path check; re-checking the resolved path means neither of those alone
|
|
622
|
-
// is load-bearing for a write outside the candidate directory.
|
|
623
|
-
if (!target.startsWith(`${candidateDir}${sep}`)) {
|
|
624
|
-
throw storeError('YMVS125', `Candidate file "${file}" resolves outside "${candidateDir}"`);
|
|
625
|
-
}
|
|
626
|
-
await mkdir(dirname(target), { recursive: true, mode: 0o700 });
|
|
627
|
-
await writePrivateFile(target, contents);
|
|
628
|
-
}
|
|
629
|
-
const diagnostics = await promotion.compile(candidateDir);
|
|
630
|
-
if (diagnostics.length > 0) {
|
|
631
|
-
return { promoted: false, candidate, candidateDir, diagnostics };
|
|
632
|
-
}
|
|
633
|
-
await writePrivateJson(paths.activeModel, {
|
|
634
|
-
format: VISUAL_ACTIVE_MODEL_FORMAT,
|
|
635
|
-
candidate,
|
|
636
|
-
authority: model.authority,
|
|
637
|
-
initialView: model.initialView,
|
|
638
|
-
promotedAt: promotion.now().toISOString(),
|
|
639
|
-
});
|
|
640
|
-
return { promoted: true, candidate, candidateDir, diagnostics: [] };
|
|
641
|
-
});
|
|
642
|
-
};
|
|
643
585
|
/**
|
|
644
586
|
* Recovers the handoff and only then deletes the session, so cleanup can never
|
|
645
587
|
* be the step that loses confirmed state. Returns `undefined` when the session
|
|
@@ -668,11 +610,10 @@ export const removeVisualSession = async (paths, includeTranscript = false) => {
|
|
|
668
610
|
* none of them could be measured.
|
|
669
611
|
*
|
|
670
612
|
* POSIX updates a directory's modification time only when its own entries
|
|
671
|
-
* change, so the session root records nothing about an appended journal
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
* keeps a symlinked artefact from reporting some other file's activity.
|
|
613
|
+
* change, so the session root records nothing about an appended journal.
|
|
614
|
+
* Every artefact that can carry activity is therefore measured directly;
|
|
615
|
+
* `lstat` keeps a symlinked artefact from reporting some other file's
|
|
616
|
+
* activity.
|
|
676
617
|
*/
|
|
677
618
|
const lastActivityMs = async (paths) => {
|
|
678
619
|
let newest;
|
|
@@ -681,8 +622,6 @@ const lastActivityMs = async (paths) => {
|
|
|
681
622
|
paths.marker,
|
|
682
623
|
paths.descriptor,
|
|
683
624
|
paths.journal,
|
|
684
|
-
paths.candidates,
|
|
685
|
-
paths.activeModel,
|
|
686
625
|
]) {
|
|
687
626
|
let entry;
|
|
688
627
|
try {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CanvasGraph } from '../../graph-projection.js';
|
|
2
|
+
import type { VISUAL_PROTOCOL_VERSION, VisualApplyResultPayload, VisualAuthority, VisualBrowserInput, VisualCapabilities, VisualChoicePresentPayload, VisualDiagnostic, VisualFilterResultPayload, VisualFreezeReason, VisualKindOption, VisualLayoutPositions, VisualLayoutSaveResultPayload, VisualResponse, VisualTerminationReason, VisualViewSaveResultPayload, VisualViewSummary } from './protocol-contract.js';
|
|
2
3
|
/**
|
|
3
4
|
* Transport shapes the session server and the browser application both speak.
|
|
4
5
|
*
|
|
@@ -9,18 +10,21 @@ import type { VISUAL_PROTOCOL_VERSION, VisualAuthority, VisualCapabilities, Visu
|
|
|
9
10
|
* that serves it — this module holds types only, and every dependency it has on
|
|
10
11
|
* the protocol is type-only too.
|
|
11
12
|
*/
|
|
12
|
-
/**
|
|
13
|
+
/** The resolved graph a session renders, as the browser receives it. */
|
|
13
14
|
export interface VisualRenderedModel {
|
|
14
|
-
readonly candidate: string;
|
|
15
15
|
readonly authority: VisualAuthority;
|
|
16
16
|
readonly initialView: string;
|
|
17
|
-
readonly
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
readonly graph: CanvasGraph;
|
|
18
|
+
/** Manifest-relative document paths — the add-concept target dropdown. */
|
|
19
|
+
readonly documents: readonly string[];
|
|
20
|
+
readonly vocabulary: {
|
|
21
|
+
readonly conceptKinds: readonly VisualKindOption[];
|
|
22
|
+
readonly relationshipKinds: readonly VisualKindOption[];
|
|
23
|
+
};
|
|
24
|
+
/** Every saved layout sidecar, keyed by projection id (Plan-level decision 1). */
|
|
25
|
+
readonly layouts: {
|
|
26
|
+
readonly [projectionId: string]: VisualLayoutPositions;
|
|
27
|
+
};
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
30
|
* One line of the conversation, as plain text.
|
|
@@ -47,6 +51,8 @@ export interface VisualSessionSnapshot {
|
|
|
47
51
|
readonly webSocketUrl: string;
|
|
48
52
|
readonly model: VisualRenderedModel;
|
|
49
53
|
readonly transcript: readonly VisualTranscriptRecord[];
|
|
54
|
+
/** The saved views the reviewer can switch this session's diagram to. */
|
|
55
|
+
readonly views: readonly VisualViewSummary[];
|
|
50
56
|
/**
|
|
51
57
|
* Whether the agent still owes an answer to something the reviewer sent. A
|
|
52
58
|
* browser that reconnects mid-turn reads this rather than inferring the turn
|
|
@@ -79,6 +85,14 @@ export type VisualServerFrame = {
|
|
|
79
85
|
readonly eventId: string;
|
|
80
86
|
} | {
|
|
81
87
|
readonly kind: 'rejected';
|
|
88
|
+
/**
|
|
89
|
+
* The input this refusal is about, as the frame that carried it named
|
|
90
|
+
* itself. A browser with a commit or a save in flight can only retire
|
|
91
|
+
* the right control if the refusal says which one died, and a refusal
|
|
92
|
+
* that never got as far as reading a type - a binary frame, a document
|
|
93
|
+
* that is not JSON - names nothing rather than guessing.
|
|
94
|
+
*/
|
|
95
|
+
readonly refused?: VisualBrowserInput['type'];
|
|
82
96
|
readonly diagnostics: readonly VisualDiagnostic[];
|
|
83
97
|
readonly frozen?: VisualFreezeReason;
|
|
84
98
|
} | {
|
|
@@ -87,6 +101,18 @@ export type VisualServerFrame = {
|
|
|
87
101
|
} | {
|
|
88
102
|
readonly kind: 'model';
|
|
89
103
|
readonly model: VisualRenderedModel;
|
|
104
|
+
} | {
|
|
105
|
+
readonly kind: 'filter-result';
|
|
106
|
+
readonly result: VisualFilterResultPayload;
|
|
107
|
+
} | {
|
|
108
|
+
readonly kind: 'view-save-result';
|
|
109
|
+
readonly result: VisualViewSaveResultPayload;
|
|
110
|
+
} | {
|
|
111
|
+
readonly kind: 'apply-result';
|
|
112
|
+
readonly result: VisualApplyResultPayload;
|
|
113
|
+
} | {
|
|
114
|
+
readonly kind: 'layout-save-result';
|
|
115
|
+
readonly result: VisualLayoutSaveResultPayload;
|
|
90
116
|
} | {
|
|
91
117
|
readonly kind: 'closing';
|
|
92
118
|
readonly reason: VisualTerminationReason;
|
|
@@ -10,6 +10,9 @@ export declare const VISUAL_SESSION_DIRECTORY: string;
|
|
|
10
10
|
/**
|
|
11
11
|
* Every command an agent can run to completion. `start` is not one of them: it
|
|
12
12
|
* blocks for the life of the session and is dispatched by `runVisualCli`.
|
|
13
|
+
*
|
|
14
|
+
* `request` is the one verb that takes no descriptor - it runs before any
|
|
15
|
+
* session exists - so it is dispatched ahead of the descriptor requirement.
|
|
13
16
|
*/
|
|
14
17
|
export declare function runVisualClientCli(args: readonly string[], cwd?: string): Promise<CliResult>;
|
|
15
18
|
/** Where a foreground session publishes, and where it observes its signals. */
|
|
@@ -3,9 +3,12 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { isMainModule, versionResult } from '../cli-support.js';
|
|
4
4
|
import { fetchVisualStatus, readVisualJsonDocument, readVisualSessionDescriptor, recoverVisualSessionClient, sendVisualResponse, stopVisualSessionClient, visualClientDiagnostic, visualFailureDiagnostics, visualSessionAlreadyStopped, waitForVisualEvent, } from './visual/client.js';
|
|
5
5
|
import { parseVisualResponse, parseVisualSessionRequest, } from './visual/protocol.js';
|
|
6
|
+
import { buildVisualSessionRequest } from './visual/request.js';
|
|
6
7
|
import { startVisualServer } from './visual/session-server.js';
|
|
7
8
|
import { pruneStaleVisualSessions } from './visual/session-store.js';
|
|
8
9
|
export const visualUsage = 'Usage:\n' +
|
|
10
|
+
' yarramate-visual request [--view <id>] [--title <text>]\n' +
|
|
11
|
+
' [--description <text>] [--chat]\n' +
|
|
9
12
|
' yarramate-visual start <request.json>\n' +
|
|
10
13
|
' yarramate-visual wait <descriptor.json> [--after <sequence>]\n' +
|
|
11
14
|
' yarramate-visual respond <descriptor.json> <response.json>\n' +
|
|
@@ -66,6 +69,63 @@ const transcriptFlag = (rest) => rest.length === 0
|
|
|
66
69
|
: rest.length === 1 && rest[0] === '--transcript'
|
|
67
70
|
? true
|
|
68
71
|
: undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Strict because the document this builds is a session's whole input: a
|
|
74
|
+
* misspelled flag that was silently ignored would open a session on the wrong
|
|
75
|
+
* view, or with chat off when the caller asked for it. A repeated flag is a
|
|
76
|
+
* caller that means two things at once, and a value beginning with `--` is a
|
|
77
|
+
* missing value rather than a title, so both refuse with usage.
|
|
78
|
+
*/
|
|
79
|
+
const requestFlags = (rest) => {
|
|
80
|
+
let view;
|
|
81
|
+
let title;
|
|
82
|
+
let description;
|
|
83
|
+
let chatEnabled = false;
|
|
84
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
85
|
+
const flag = rest[index];
|
|
86
|
+
if (flag === '--chat') {
|
|
87
|
+
if (chatEnabled)
|
|
88
|
+
return undefined;
|
|
89
|
+
chatEnabled = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const value = rest[index + 1];
|
|
93
|
+
if (value === undefined || value.startsWith('--'))
|
|
94
|
+
return undefined;
|
|
95
|
+
index += 1;
|
|
96
|
+
if (flag === '--view' && view === undefined)
|
|
97
|
+
view = value;
|
|
98
|
+
else if (flag === '--title' && title === undefined)
|
|
99
|
+
title = value;
|
|
100
|
+
else if (flag === '--description' && description === undefined) {
|
|
101
|
+
description = value;
|
|
102
|
+
}
|
|
103
|
+
else
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
return { view, title, description, chatEnabled };
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Transcribes the workspace into the request document `start` consumes. It is
|
|
110
|
+
* the only visual command that reads the repository rather than a session:
|
|
111
|
+
* every other one addresses a descriptor, and this one runs before a session
|
|
112
|
+
* exists.
|
|
113
|
+
*/
|
|
114
|
+
const requestCliCommand = (rest, cwd) => {
|
|
115
|
+
const flags = requestFlags(rest);
|
|
116
|
+
if (flags === undefined)
|
|
117
|
+
return usageResult;
|
|
118
|
+
const built = buildVisualSessionRequest({
|
|
119
|
+
cwd,
|
|
120
|
+
initialView: flags.view,
|
|
121
|
+
title: flags.title,
|
|
122
|
+
description: flags.description,
|
|
123
|
+
chatEnabled: flags.chatEnabled,
|
|
124
|
+
});
|
|
125
|
+
if (!built.ok)
|
|
126
|
+
return refusalResult(built.diagnostics);
|
|
127
|
+
return { exitCode: 0, stdout: documentLine(built.request), stderr: '' };
|
|
128
|
+
};
|
|
69
129
|
const waitCliCommand = async (descriptorPath, rest, cwd) => {
|
|
70
130
|
const after = sequenceFlag(rest);
|
|
71
131
|
if (after === undefined)
|
|
@@ -164,11 +224,16 @@ const stopCliCommand = async (descriptorPath, rest, cwd) => {
|
|
|
164
224
|
/**
|
|
165
225
|
* Every command an agent can run to completion. `start` is not one of them: it
|
|
166
226
|
* blocks for the life of the session and is dispatched by `runVisualCli`.
|
|
227
|
+
*
|
|
228
|
+
* `request` is the one verb that takes no descriptor - it runs before any
|
|
229
|
+
* session exists - so it is dispatched ahead of the descriptor requirement.
|
|
167
230
|
*/
|
|
168
231
|
export async function runVisualClientCli(args, cwd = process.cwd()) {
|
|
169
232
|
const [command, descriptor, ...rest] = args;
|
|
170
233
|
if (command === '--version')
|
|
171
234
|
return versionResult('yarramate-visual');
|
|
235
|
+
if (command === 'request')
|
|
236
|
+
return requestCliCommand(args.slice(1), cwd);
|
|
172
237
|
if (descriptor === undefined)
|
|
173
238
|
return usageResult;
|
|
174
239
|
switch (command) {
|
|
@@ -215,7 +280,7 @@ export const runVisualStart = async (requestPath, cwd = process.cwd(), io = proc
|
|
|
215
280
|
// A previous runtime's orphans are collected on the critical path of a new
|
|
216
281
|
// session, bounded per pass so a full directory cannot stall this one.
|
|
217
282
|
await pruneStaleVisualSessions(baseDir, new Date());
|
|
218
|
-
handle = await startVisualServer({ request: request.value, baseDir });
|
|
283
|
+
handle = await startVisualServer({ request: request.value, baseDir, cwd });
|
|
219
284
|
}
|
|
220
285
|
catch (cause) {
|
|
221
286
|
// A failed start has already recovered and removed whatever it created.
|
package/dist/apply-command.d.ts
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
import { type CliResult } from './cli-support.js';
|
|
2
|
+
import { type Diagnostic, type WorkspaceSource } from './compiler.js';
|
|
3
|
+
import type { YarramateApplyResult } from './operations.js';
|
|
4
|
+
export type ApplyOutcome = {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly result: YarramateApplyResult;
|
|
7
|
+
} | {
|
|
8
|
+
readonly ok: false;
|
|
9
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
10
|
+
};
|
|
11
|
+
export declare const applyOperations: (operations: WorkspaceSource, workspace: WorkspaceSource, cwd: string) => ApplyOutcome;
|
|
2
12
|
export declare function runApplyCommand(options: readonly string[], cwd: string): CliResult;
|