@principal-ai/subsystems-react 0.38.0 → 0.40.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/components/maintain-events/MaintainEventLog.d.ts +35 -0
- package/dist/components/maintain-events/MaintainEventLog.d.ts.map +1 -0
- package/dist/components/maintain-events/MaintainEventLog.js +66 -0
- package/dist/components/maintain-events/MaintainEventLog.js.map +1 -0
- package/dist/components/maintain-events/MaintainLivePanel.d.ts +15 -0
- package/dist/components/maintain-events/MaintainLivePanel.d.ts.map +1 -0
- package/dist/components/maintain-events/MaintainLivePanel.js +49 -0
- package/dist/components/maintain-events/MaintainLivePanel.js.map +1 -0
- package/dist/components/maintain-events/index.d.ts +8 -0
- package/dist/components/maintain-events/index.d.ts.map +1 -0
- package/dist/components/maintain-events/index.js +6 -0
- package/dist/components/maintain-events/index.js.map +1 -0
- package/dist/graphify/consolidated.d.ts +8 -0
- package/dist/graphify/consolidated.d.ts.map +1 -1
- package/dist/graphify/construct.d.ts +18 -0
- package/dist/graphify/construct.d.ts.map +1 -1
- package/dist/graphify/construct.js +23 -0
- package/dist/graphify/construct.js.map +1 -1
- package/dist/graphify/index.d.ts +2 -2
- package/dist/graphify/index.d.ts.map +1 -1
- package/dist/graphify/index.js +1 -1
- package/dist/graphify/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/subsystem/IssueList.d.ts +49 -3
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +180 -79
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +15 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +464 -14
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/formatDeclaration.js +19 -5
- package/dist/subsystem/formatDeclaration.js.map +1 -1
- package/dist/subsystem/model.d.ts +38 -0
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +124 -67
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/subsystem/symbolRefs.d.ts.map +1 -1
- package/dist/subsystem/symbolRefs.js +2 -0
- package/dist/subsystem/symbolRefs.js.map +1 -1
- package/package.json +3 -3
- package/src/components/maintain-events/MaintainEventLog.tsx +152 -0
- package/src/components/maintain-events/MaintainLivePanel.tsx +99 -0
- package/src/components/maintain-events/index.ts +10 -0
- package/src/graphify/consolidated.ts +8 -0
- package/src/graphify/construct.test.ts +61 -1
- package/src/graphify/construct.ts +33 -0
- package/src/graphify/index.ts +6 -1
- package/src/index.ts +9 -0
- package/src/stories/MaintainEvents/MaintainEventLog.stories.tsx +66 -0
- package/src/stories/MaintainEvents/MaintainLivePanel.stories.tsx +77 -0
- package/src/stories/Subsystem/ComponentGraph/IssueOverlay.stories.tsx +625 -0
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +7 -1
- package/src/stories/Subsystem/ComponentGraph/NodeAnatomy.stories.tsx +40 -73
- package/src/stories/Subsystem/ComponentGraph/StoreValueType.stories.tsx +261 -0
- package/src/stories/Subsystem/StoreValueTypeDeclaration.stories.tsx +182 -0
- package/src/stories/data/maintain-live-events.json +554 -0
- package/src/subsystem/IssueList.focus.test.tsx +113 -0
- package/src/subsystem/IssueList.icon.test.tsx +95 -0
- package/src/subsystem/IssueList.tsx +267 -111
- package/src/subsystem/SubsystemComponentGraph.tsx +541 -13
- package/src/subsystem/formatDeclaration.test.ts +64 -0
- package/src/subsystem/formatDeclaration.ts +20 -5
- package/src/subsystem/model.ts +46 -0
- package/src/subsystem/nodes.group.test.tsx +52 -0
- package/src/subsystem/nodes.tsx +102 -2
- package/src/subsystem/symbolRefs.ts +2 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaintainEventLog — presentational live event log for an OpenCode agent run.
|
|
3
|
+
*
|
|
4
|
+
* Renders one row per event as `time type — summary`, with a header carrying
|
|
5
|
+
* title / agent / session id / status / counts. Subsystems Studio's
|
|
6
|
+
* `MaintainEventsView` owns the RPC + subscription wiring and feeds the
|
|
7
|
+
* resulting state in as props; keeping the wiring out of this component lets it
|
|
8
|
+
* render in Storybook and embed in the subsystem graph sidebar.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useEffect, useRef, type CSSProperties } from 'react';
|
|
12
|
+
import { useTheme } from '@principal-ade/industry-theme';
|
|
13
|
+
|
|
14
|
+
export interface MaintainEventLogEvent {
|
|
15
|
+
at: number;
|
|
16
|
+
type: string;
|
|
17
|
+
sessionId?: string;
|
|
18
|
+
summary: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MaintainEventLogProps {
|
|
22
|
+
title?: string;
|
|
23
|
+
agent?: string;
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
status: string;
|
|
26
|
+
events: MaintainEventLogEvent[];
|
|
27
|
+
total: number;
|
|
28
|
+
error?: string | null;
|
|
29
|
+
/** Shown before a session id is known. @default 'Waiting for OpenCode session…' */
|
|
30
|
+
waitingLabel?: string;
|
|
31
|
+
/** Shown once a session exists but no events have arrived. @default 'Waiting for SSE events…' */
|
|
32
|
+
emptyLabel?: string;
|
|
33
|
+
/** Keep the log pinned to the newest event. @default true */
|
|
34
|
+
autoScroll?: boolean;
|
|
35
|
+
/** Render the title/status header above the log. @default true */
|
|
36
|
+
showHeader?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatTime(at: number): string {
|
|
40
|
+
return new Date(at).toLocaleTimeString(undefined, {
|
|
41
|
+
hour: '2-digit',
|
|
42
|
+
minute: '2-digit',
|
|
43
|
+
second: '2-digit',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function MaintainEventLog({
|
|
48
|
+
title,
|
|
49
|
+
agent,
|
|
50
|
+
sessionId,
|
|
51
|
+
status,
|
|
52
|
+
events,
|
|
53
|
+
total,
|
|
54
|
+
error,
|
|
55
|
+
waitingLabel = 'Waiting for OpenCode session…',
|
|
56
|
+
emptyLabel = 'Waiting for SSE events…',
|
|
57
|
+
autoScroll = true,
|
|
58
|
+
showHeader = true,
|
|
59
|
+
}: MaintainEventLogProps) {
|
|
60
|
+
const { theme } = useTheme();
|
|
61
|
+
const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
|
|
62
|
+
const logRef = useRef<HTMLDivElement | null>(null);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!autoScroll) return;
|
|
66
|
+
const el = logRef.current;
|
|
67
|
+
if (!el) return;
|
|
68
|
+
el.scrollTop = el.scrollHeight;
|
|
69
|
+
}, [autoScroll, events.length]);
|
|
70
|
+
|
|
71
|
+
if (!sessionId) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
style={{
|
|
75
|
+
width: '100%',
|
|
76
|
+
height: '100%',
|
|
77
|
+
display: 'flex',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
justifyContent: 'center',
|
|
80
|
+
color: muted,
|
|
81
|
+
fontFamily: theme.fonts.body,
|
|
82
|
+
fontSize: theme.fontSizes[1],
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{waitingLabel}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const wrap: CSSProperties = {
|
|
91
|
+
height: '100%',
|
|
92
|
+
display: 'flex',
|
|
93
|
+
flexDirection: 'column',
|
|
94
|
+
padding: 20,
|
|
95
|
+
color: theme.colors.text,
|
|
96
|
+
fontFamily: theme.fonts.body,
|
|
97
|
+
boxSizing: 'border-box',
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<div style={wrap}>
|
|
102
|
+
{showHeader ? (
|
|
103
|
+
<div style={{ marginBottom: 12 }}>
|
|
104
|
+
<div style={{ fontSize: theme.fontSizes[3], fontWeight: 650 }}>
|
|
105
|
+
{title ?? 'Maintain events'}
|
|
106
|
+
</div>
|
|
107
|
+
<div style={{ marginTop: 4, fontSize: theme.fontSizes[0], color: muted }}>
|
|
108
|
+
{agent ? `${agent} · ` : null}
|
|
109
|
+
{sessionId}
|
|
110
|
+
{' · '}
|
|
111
|
+
<span style={{ fontWeight: 600 }}>{status}</span>
|
|
112
|
+
{' · '}
|
|
113
|
+
{total} events
|
|
114
|
+
{events.length < total ? ` (showing last ${events.length})` : null}
|
|
115
|
+
</div>
|
|
116
|
+
{error ? (
|
|
117
|
+
<div style={{ marginTop: 8, color: theme.colors.error, fontSize: theme.fontSizes[0] }}>
|
|
118
|
+
{error}
|
|
119
|
+
</div>
|
|
120
|
+
) : null}
|
|
121
|
+
</div>
|
|
122
|
+
) : null}
|
|
123
|
+
<div
|
|
124
|
+
ref={logRef}
|
|
125
|
+
style={{
|
|
126
|
+
flex: 1,
|
|
127
|
+
minHeight: 0,
|
|
128
|
+
overflow: 'auto',
|
|
129
|
+
borderRadius: 8,
|
|
130
|
+
border: `1px solid ${theme.colors.border ?? 'rgba(255,255,255,0.12)'}`,
|
|
131
|
+
background: 'rgba(0,0,0,0.25)',
|
|
132
|
+
padding: '10px 12px',
|
|
133
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
|
|
134
|
+
fontSize: theme.fontSizes[0],
|
|
135
|
+
lineHeight: 1.45,
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
{events.length === 0 ? (
|
|
139
|
+
<div style={{ color: muted }}>{emptyLabel}</div>
|
|
140
|
+
) : (
|
|
141
|
+
events.map((ev, i) => (
|
|
142
|
+
<div key={`${ev.at}-${ev.type}-${i}`} style={{ marginBottom: 6 }}>
|
|
143
|
+
<span style={{ color: muted }}>{formatTime(ev.at)}</span>{' '}
|
|
144
|
+
<span style={{ color: theme.colors.primary ?? '#8fd' }}>{ev.type}</span>
|
|
145
|
+
{ev.summary && ev.summary !== ev.type ? <span> — {ev.summary}</span> : null}
|
|
146
|
+
</div>
|
|
147
|
+
))
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaintainLivePanel — a collapsible, half-height panel that overlays the graph
|
|
3
|
+
* canvas and streams a live agent run's events. Subsystems Studio supplies the
|
|
4
|
+
* state (status/events/total/error) and renders it through
|
|
5
|
+
* `SubsystemComponentGraph`'s `liveEvents` prop; collapsing leaves just the
|
|
6
|
+
* header bar so the graph stays readable.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import { useTheme } from '@principal-ade/industry-theme';
|
|
11
|
+
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
12
|
+
import { MaintainEventLog, type MaintainEventLogProps } from './MaintainEventLog';
|
|
13
|
+
|
|
14
|
+
export interface MaintainLivePanelProps extends MaintainEventLogProps {
|
|
15
|
+
/** Start collapsed (header only). @default false */
|
|
16
|
+
defaultCollapsed?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const HEADER_HEIGHT = 36;
|
|
20
|
+
|
|
21
|
+
export function MaintainLivePanel({
|
|
22
|
+
title,
|
|
23
|
+
agent,
|
|
24
|
+
sessionId,
|
|
25
|
+
status,
|
|
26
|
+
events,
|
|
27
|
+
total,
|
|
28
|
+
error,
|
|
29
|
+
defaultCollapsed = false,
|
|
30
|
+
}: MaintainLivePanelProps) {
|
|
31
|
+
const { theme } = useTheme();
|
|
32
|
+
const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
|
|
33
|
+
const border = theme.colors.border ?? 'rgba(255,255,255,0.12)';
|
|
34
|
+
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
style={{
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
left: 0,
|
|
41
|
+
right: 0,
|
|
42
|
+
bottom: 0,
|
|
43
|
+
height: collapsed ? HEADER_HEIGHT : '50%',
|
|
44
|
+
zIndex: 5,
|
|
45
|
+
display: 'flex',
|
|
46
|
+
flexDirection: 'column',
|
|
47
|
+
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
48
|
+
borderTop: `1px solid ${border}`,
|
|
49
|
+
boxShadow: '0 -8px 24px rgba(0,0,0,0.35)',
|
|
50
|
+
overflow: 'hidden',
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
onClick={() => setCollapsed((c) => !c)}
|
|
56
|
+
style={{
|
|
57
|
+
display: 'flex',
|
|
58
|
+
alignItems: 'center',
|
|
59
|
+
gap: 8,
|
|
60
|
+
height: HEADER_HEIGHT,
|
|
61
|
+
flexShrink: 0,
|
|
62
|
+
padding: '0 12px',
|
|
63
|
+
border: 'none',
|
|
64
|
+
borderBottom: collapsed ? 'none' : `1px solid ${border}`,
|
|
65
|
+
background: 'transparent',
|
|
66
|
+
color: theme.colors.text,
|
|
67
|
+
fontFamily: theme.fonts.body,
|
|
68
|
+
fontSize: theme.fontSizes[0],
|
|
69
|
+
cursor: 'pointer',
|
|
70
|
+
textAlign: 'left',
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{collapsed ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
|
|
74
|
+
<span style={{ fontWeight: 600 }}>Live events</span>
|
|
75
|
+
{agent ? <span style={{ color: muted }}>{agent}</span> : null}
|
|
76
|
+
{status ? <span style={{ color: muted }}>· {status}</span> : null}
|
|
77
|
+
{total > 0 ? <span style={{ color: muted }}>· {total} events</span> : null}
|
|
78
|
+
<span style={{ flex: 1 }} />
|
|
79
|
+
{sessionId ? (
|
|
80
|
+
<span style={{ color: muted, fontFamily: theme.fonts.monospace }}>{sessionId}</span>
|
|
81
|
+
) : null}
|
|
82
|
+
</button>
|
|
83
|
+
{!collapsed ? (
|
|
84
|
+
<div style={{ flex: 1, minHeight: 0 }}>
|
|
85
|
+
<MaintainEventLog
|
|
86
|
+
title={title}
|
|
87
|
+
agent={agent}
|
|
88
|
+
sessionId={sessionId}
|
|
89
|
+
status={status}
|
|
90
|
+
events={events}
|
|
91
|
+
total={total}
|
|
92
|
+
error={error}
|
|
93
|
+
showHeader={false}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
) : null}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maintain event log — presentational live OpenCode agent-run event feed.
|
|
3
|
+
*/
|
|
4
|
+
export { MaintainEventLog } from './MaintainEventLog';
|
|
5
|
+
export type {
|
|
6
|
+
MaintainEventLogEvent,
|
|
7
|
+
MaintainEventLogProps,
|
|
8
|
+
} from './MaintainEventLog';
|
|
9
|
+
export { MaintainLivePanel } from './MaintainLivePanel';
|
|
10
|
+
export type { MaintainLivePanelProps } from './MaintainLivePanel';
|
|
@@ -240,6 +240,14 @@ export interface GraphifyStoreDetail {
|
|
|
240
240
|
* system (db/service; carries no `process`).
|
|
241
241
|
*/
|
|
242
242
|
storage?: 'memory' | 'disk' | 'external';
|
|
243
|
+
/**
|
|
244
|
+
* The type of the retained state itself — a store declares its type the way
|
|
245
|
+
* every other declaration does (`Map<string, FeedState>`, `Set<Listener>`,
|
|
246
|
+
* a table's row type). Orthogonal to `storage`.
|
|
247
|
+
*/
|
|
248
|
+
valueType?: string;
|
|
249
|
+
/** Backing edge target for `valueType`, when the graph has one. */
|
|
250
|
+
valueTypeRef?: GraphifyReferenceInfo;
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
/** A standalone method selected from its owning class. */
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
import type { GraphifyEdge, GraphifyNode } from './types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
inferConstructFromGraphify,
|
|
5
|
+
constructsMatch,
|
|
6
|
+
resolveConstructMatch,
|
|
7
|
+
} from './construct';
|
|
4
8
|
|
|
5
9
|
function node(
|
|
6
10
|
id: string,
|
|
@@ -106,3 +110,59 @@ describe('constructsMatch', () => {
|
|
|
106
110
|
expect(constructsMatch('function', 'class')).toBe(false);
|
|
107
111
|
});
|
|
108
112
|
});
|
|
113
|
+
|
|
114
|
+
describe('resolveConstructMatch', () => {
|
|
115
|
+
test('structural match is enough (no augmentation)', () => {
|
|
116
|
+
expect(resolveConstructMatch('function', 'function')).toEqual({
|
|
117
|
+
match: true,
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('plain mismatch without augmentation stays a hard failure', () => {
|
|
122
|
+
expect(resolveConstructMatch('function', 'class')).toEqual({
|
|
123
|
+
match: false,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('unknown without augmentation is unconfirmed, not a mismatch', () => {
|
|
128
|
+
expect(resolveConstructMatch('store', 'unknown')).toEqual({ match: null });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('accepted augmentation confirms a claim Graphify infers differently', () => {
|
|
132
|
+
expect(resolveConstructMatch('store', 'function', 'store')).toEqual({
|
|
133
|
+
match: true,
|
|
134
|
+
augmentedBy: 'store',
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('accepted augmentation confirms a claim Graphify leaves unknown', () => {
|
|
139
|
+
expect(resolveConstructMatch('interface', 'unknown', 'interface')).toEqual({
|
|
140
|
+
match: true,
|
|
141
|
+
augmentedBy: 'interface',
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('augmentation only confirms its own claim, not a different one', () => {
|
|
146
|
+
expect(resolveConstructMatch('store', 'function', 'module')).toEqual({
|
|
147
|
+
match: false,
|
|
148
|
+
});
|
|
149
|
+
expect(resolveConstructMatch('store', 'unknown', 'module')).toEqual({
|
|
150
|
+
match: null,
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('blank augmentation is ignored', () => {
|
|
155
|
+
expect(resolveConstructMatch('store', 'function', ' ')).toEqual({
|
|
156
|
+
match: false,
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('external / missing claimed still short-circuits to match', () => {
|
|
161
|
+
expect(resolveConstructMatch('external', 'function')).toEqual({
|
|
162
|
+
match: true,
|
|
163
|
+
});
|
|
164
|
+
expect(resolveConstructMatch(undefined, 'unknown')).toEqual({
|
|
165
|
+
match: true,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -118,3 +118,36 @@ export function constructsMatch(
|
|
|
118
118
|
if (TYPE_FAMILY.has(claimed)) return inferred === 'type';
|
|
119
119
|
return claimed === inferred;
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
export interface ConstructCheckOutcome {
|
|
123
|
+
/** true = confirmed, false = known mismatch, null = unconfirmed (needs agent). */
|
|
124
|
+
match: boolean | null;
|
|
125
|
+
/** Confirming construct from an accepted augmentation, when that is what confirmed it. */
|
|
126
|
+
augmentedBy?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resolve a claimed construct against Graphify's inferred construct, allowing an
|
|
131
|
+
* accepted, agent-confirmed augmentation to stand in for Graphify when it is
|
|
132
|
+
* silent (`unknown`) or when it disagrees.
|
|
133
|
+
*
|
|
134
|
+
* Graphify's inferred construct is a *structural hint*, never ground truth: its
|
|
135
|
+
* vocabulary (`class | function | method | type | module | unknown`) has no
|
|
136
|
+
* `store` or `custom_entity`, and a call-style accessor label biases it to
|
|
137
|
+
* `function`. When source-reading confirms the model's claim, that confirmation
|
|
138
|
+
* (recorded as an accepted augmentation) wins over the hint.
|
|
139
|
+
*/
|
|
140
|
+
export function resolveConstructMatch(
|
|
141
|
+
claimed: string | undefined,
|
|
142
|
+
inferred: InferredGraphifyConstruct,
|
|
143
|
+
augmentedConstruct?: string | null,
|
|
144
|
+
): ConstructCheckOutcome {
|
|
145
|
+
const aug = augmentedConstruct?.trim();
|
|
146
|
+
if (!claimed || claimed === 'external') return { match: true };
|
|
147
|
+
const augConfirms = !!aug && aug === claimed;
|
|
148
|
+
if (inferred === 'unknown') {
|
|
149
|
+
return augConfirms ? { match: true, augmentedBy: aug } : { match: null };
|
|
150
|
+
}
|
|
151
|
+
if (constructsMatch(claimed, inferred)) return { match: true };
|
|
152
|
+
return augConfirms ? { match: true, augmentedBy: aug } : { match: false };
|
|
153
|
+
}
|
package/src/graphify/index.ts
CHANGED
|
@@ -61,8 +61,13 @@ export {
|
|
|
61
61
|
export type {
|
|
62
62
|
InferredGraphifyConstruct,
|
|
63
63
|
InferConstructFromGraphifyResult,
|
|
64
|
+
ConstructCheckOutcome,
|
|
65
|
+
} from './construct';
|
|
66
|
+
export {
|
|
67
|
+
inferConstructFromGraphify,
|
|
68
|
+
constructsMatch,
|
|
69
|
+
resolveConstructMatch,
|
|
64
70
|
} from './construct';
|
|
65
|
-
export { inferConstructFromGraphify, constructsMatch } from './construct';
|
|
66
71
|
export type {
|
|
67
72
|
GraphifyInferredSignature,
|
|
68
73
|
ClaimedSignature,
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,14 @@ export type {
|
|
|
16
16
|
SessionEventFeedRow,
|
|
17
17
|
} from './components/session-events';
|
|
18
18
|
|
|
19
|
+
// Maintain event log — presentational live OpenCode agent-run event feed
|
|
20
|
+
export { MaintainEventLog, MaintainLivePanel } from './components/maintain-events';
|
|
21
|
+
export type {
|
|
22
|
+
MaintainEventLogEvent,
|
|
23
|
+
MaintainEventLogProps,
|
|
24
|
+
MaintainLivePanelProps,
|
|
25
|
+
} from './components/maintain-events';
|
|
26
|
+
|
|
19
27
|
// Graphify integration types (graph.json data model)
|
|
20
28
|
export type {
|
|
21
29
|
JsonValue,
|
|
@@ -62,6 +70,7 @@ export {
|
|
|
62
70
|
symbolLabelVariants,
|
|
63
71
|
inferConstructFromGraphify,
|
|
64
72
|
constructsMatch,
|
|
73
|
+
resolveConstructMatch,
|
|
65
74
|
extractNamedTypes,
|
|
66
75
|
extractGraphifySignature,
|
|
67
76
|
compareSignatures,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maintain Event Log — presentational live agent-run event feed.
|
|
3
|
+
*
|
|
4
|
+
* The `Live` story replays a fixture derived from a real OpenCode V2 agent
|
|
5
|
+
* session (`packages/subsystems-core/src/opencode/__fixtures__/session-events.json`),
|
|
6
|
+
* mapped to the live-feed vocabulary (`session.next.*` / `session.status`) and
|
|
7
|
+
* summarized the same way the host's `/api/event` subscription is
|
|
8
|
+
* (`summarizeEvent` in `subsystems-studio/src/bun/opencode-v2-live.ts`).
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
12
|
+
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
13
|
+
import {
|
|
14
|
+
MaintainEventLog,
|
|
15
|
+
type MaintainEventLogEvent,
|
|
16
|
+
} from '../../components/maintain-events';
|
|
17
|
+
import liveEvents from '../data/maintain-live-events.json';
|
|
18
|
+
|
|
19
|
+
const events = liveEvents as MaintainEventLogEvent[];
|
|
20
|
+
|
|
21
|
+
const meta = {
|
|
22
|
+
title: 'Agent Sessions/Maintain Event Log',
|
|
23
|
+
component: MaintainEventLog,
|
|
24
|
+
parameters: { layout: 'fullscreen' },
|
|
25
|
+
tags: ['autodocs'],
|
|
26
|
+
decorators: [
|
|
27
|
+
(Story) => (
|
|
28
|
+
<ThemeProvider theme={defaultEditorTheme}>
|
|
29
|
+
<div
|
|
30
|
+
style={{
|
|
31
|
+
height: '100vh',
|
|
32
|
+
background: defaultEditorTheme.colors.background,
|
|
33
|
+
color: defaultEditorTheme.colors.text,
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
<Story />
|
|
37
|
+
</div>
|
|
38
|
+
</ThemeProvider>
|
|
39
|
+
),
|
|
40
|
+
],
|
|
41
|
+
args: {
|
|
42
|
+
title: 'Maintain events',
|
|
43
|
+
agent: 'construct-verifier',
|
|
44
|
+
sessionId: 'ses_056b1f037ffeu1d00DzvJBrHq9',
|
|
45
|
+
status: 'running',
|
|
46
|
+
events,
|
|
47
|
+
total: events.length,
|
|
48
|
+
},
|
|
49
|
+
} satisfies Meta<typeof MaintainEventLog>;
|
|
50
|
+
|
|
51
|
+
export default meta;
|
|
52
|
+
type Story = StoryObj<typeof meta>;
|
|
53
|
+
|
|
54
|
+
export const Live: Story = {};
|
|
55
|
+
|
|
56
|
+
export const WaitingForSession: Story = {
|
|
57
|
+
args: { sessionId: undefined, events: [], total: 0, status: 'starting' },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const WaitingForEvents: Story = {
|
|
61
|
+
args: { events: [], total: 0 },
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const WithError: Story = {
|
|
65
|
+
args: { error: 'live event stream dropped — reconnecting and reconciling with server state' },
|
|
66
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maintain Live Panel — the collapsible half-height live-agent-run panel that
|
|
3
|
+
* `SubsystemComponentGraph` renders over its canvas via the `liveEvents` prop.
|
|
4
|
+
*
|
|
5
|
+
* The fixture replays a real OpenCode V2 agent session
|
|
6
|
+
* (`packages/subsystems-core/src/opencode/__fixtures__/session-events.json`),
|
|
7
|
+
* mapped to the live-feed vocabulary and summarized like the host's
|
|
8
|
+
* `/api/event` subscription.
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
12
|
+
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
13
|
+
import {
|
|
14
|
+
MaintainLivePanel,
|
|
15
|
+
type MaintainEventLogEvent,
|
|
16
|
+
} from '../../components/maintain-events';
|
|
17
|
+
import liveEvents from '../data/maintain-live-events.json';
|
|
18
|
+
|
|
19
|
+
const events = liveEvents as MaintainEventLogEvent[];
|
|
20
|
+
|
|
21
|
+
const meta = {
|
|
22
|
+
title: 'Agent Sessions/Maintain Live Panel',
|
|
23
|
+
component: MaintainLivePanel,
|
|
24
|
+
parameters: { layout: 'fullscreen' },
|
|
25
|
+
tags: ['autodocs'],
|
|
26
|
+
decorators: [
|
|
27
|
+
(Story) => (
|
|
28
|
+
<ThemeProvider theme={defaultEditorTheme}>
|
|
29
|
+
<div
|
|
30
|
+
style={{
|
|
31
|
+
position: 'relative',
|
|
32
|
+
height: '100vh',
|
|
33
|
+
overflow: 'hidden',
|
|
34
|
+
background: defaultEditorTheme.colors.background,
|
|
35
|
+
color: defaultEditorTheme.colors.text,
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
style={{
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
inset: 0,
|
|
42
|
+
display: 'grid',
|
|
43
|
+
placeItems: 'center',
|
|
44
|
+
color: defaultEditorTheme.colors.textMuted,
|
|
45
|
+
fontFamily: defaultEditorTheme.fonts.monospace,
|
|
46
|
+
fontSize: 13,
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
graph canvas
|
|
50
|
+
</div>
|
|
51
|
+
<Story />
|
|
52
|
+
</div>
|
|
53
|
+
</ThemeProvider>
|
|
54
|
+
),
|
|
55
|
+
],
|
|
56
|
+
args: {
|
|
57
|
+
title: 'Maintain events',
|
|
58
|
+
agent: 'construct-verifier',
|
|
59
|
+
sessionId: 'ses_056b1f037ffeu1d00DzvJBrHq9',
|
|
60
|
+
status: 'running',
|
|
61
|
+
events,
|
|
62
|
+
total: events.length,
|
|
63
|
+
},
|
|
64
|
+
} satisfies Meta<typeof MaintainLivePanel>;
|
|
65
|
+
|
|
66
|
+
export default meta;
|
|
67
|
+
type Story = StoryObj<typeof meta>;
|
|
68
|
+
|
|
69
|
+
export const Expanded: Story = {};
|
|
70
|
+
|
|
71
|
+
export const Collapsed: Story = {
|
|
72
|
+
args: { defaultCollapsed: true },
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const WaitingForEvents: Story = {
|
|
76
|
+
args: { events: [], total: 0 },
|
|
77
|
+
};
|