@volter-ai-dev/supercode-ui 0.1.70 → 0.1.72
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 +61 -1
- package/activity.mjs +5 -1235
- package/chunks/chunk-2FG7VKIW.mjs +201 -0
- package/chunks/chunk-4XUMCWPC.mjs +29 -0
- package/chunks/chunk-E65I2I2L.mjs +120 -0
- package/chunks/chunk-IL5T2AST.mjs +842 -0
- package/chunks/chunk-LOP6XOON.mjs +671 -0
- package/chunks/chunk-LS6JBXNT.mjs +1237 -0
- package/chunks/chunk-SJ3YTA6Z.mjs +316 -0
- package/chunks/chunk-TKTOVDXB.mjs +139 -0
- package/chunks/chunk-V3UM7H7W.mjs +328 -0
- package/chunks/chunk-WRK5TBX3.mjs +224 -0
- package/chunks/chunk-ZLYHUYE2.mjs +62 -0
- package/components.d.ts +3 -0
- package/components.mjs +59 -3766
- package/composer.mjs +8 -580
- package/conversation.mjs +16 -1373
- package/embed.mjs +15 -3681
- package/icon.mjs +3 -58
- package/index.d.ts +25 -0
- package/logo.mjs +5 -87
- package/messenger.d.ts +3 -0
- package/messenger.mjs +21 -3677
- package/package.json +22 -3
- package/react/activity.mjs +5 -1235
- package/react/chunks/chunk-7ES5FSLG.mjs +316 -0
- package/react/chunks/chunk-LS6JBXNT.mjs +1237 -0
- package/react/chunks/chunk-LYRIQE5M.mjs +29 -0
- package/react/chunks/chunk-NJC6S2AW.mjs +201 -0
- package/react/chunks/chunk-OINC7LV7.mjs +62 -0
- package/react/chunks/chunk-QL6QEYPK.mjs +842 -0
- package/react/chunks/chunk-UGL6AB2K.mjs +120 -0
- package/react/chunks/chunk-UKHDCPME.mjs +328 -0
- package/react/chunks/chunk-UKMTRNJN.mjs +671 -0
- package/react/chunks/chunk-VFOVJ4S3.mjs +139 -0
- package/react/chunks/chunk-YK7SJ6EH.mjs +224 -0
- package/react/components.mjs +59 -3766
- package/react/composer.mjs +8 -580
- package/react/conversation.mjs +16 -1373
- package/react/icon.mjs +3 -58
- package/react/index.d.ts +25 -0
- package/react/logo.mjs +5 -87
- package/react/messenger.d.ts +3 -0
- package/react/messenger.mjs +21 -3677
- package/react/sessions.mjs +9 -519
- package/react/settings.mjs +9 -425
- package/react/subagents.mjs +8 -1546
- package/react/supervision-components.mjs +1354 -0
- package/react/supervision.d.ts +36 -0
- package/sessions.mjs +9 -519
- package/settings.mjs +9 -425
- package/source-inventory.d.ts +240 -0
- package/source-inventory.mjs +367 -0
- package/styles.css +104 -0
- package/subagents.mjs +8 -1546
- package/supervision-components.d.ts +36 -0
- package/supervision-components.mjs +1354 -0
- package/supervision.d.ts +242 -0
- package/supervision.mjs +349 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
export type SourceSection =
|
|
2
|
+
"profiles" | "channels" | "routes" | "triggers" | "skills" | "memory";
|
|
3
|
+
export interface InventoryPage<T> {
|
|
4
|
+
status: "ready" | "loading" | "error" | "unavailable";
|
|
5
|
+
items: T[];
|
|
6
|
+
total: number;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ProfileModel {
|
|
10
|
+
key: string;
|
|
11
|
+
name: string;
|
|
12
|
+
harness: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
isDefault: boolean;
|
|
15
|
+
routes: number | null;
|
|
16
|
+
sessions: number | null;
|
|
17
|
+
model: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface ChannelModel {
|
|
20
|
+
key: string;
|
|
21
|
+
name: string;
|
|
22
|
+
harness: string;
|
|
23
|
+
kind: string;
|
|
24
|
+
account: string | null;
|
|
25
|
+
enabled: boolean | null;
|
|
26
|
+
configured: boolean | null;
|
|
27
|
+
connection: string;
|
|
28
|
+
sessions: number | null;
|
|
29
|
+
}
|
|
30
|
+
export interface RouteModel {
|
|
31
|
+
key: string;
|
|
32
|
+
harness: string;
|
|
33
|
+
target: string;
|
|
34
|
+
profileKey: string;
|
|
35
|
+
specificity: number | null;
|
|
36
|
+
isDefault: boolean;
|
|
37
|
+
match: Record<string, string>;
|
|
38
|
+
}
|
|
39
|
+
export interface TriggerModel {
|
|
40
|
+
key: string;
|
|
41
|
+
name: string;
|
|
42
|
+
harness: string;
|
|
43
|
+
kind: string;
|
|
44
|
+
route: string;
|
|
45
|
+
events: string[];
|
|
46
|
+
profile: string | null;
|
|
47
|
+
profileKey: string | null;
|
|
48
|
+
action: string | null;
|
|
49
|
+
session: string | null;
|
|
50
|
+
destination: string | null;
|
|
51
|
+
enabled: boolean | null;
|
|
52
|
+
authenticated: boolean | null;
|
|
53
|
+
description: string;
|
|
54
|
+
}
|
|
55
|
+
export interface SkillModel {
|
|
56
|
+
key: string;
|
|
57
|
+
name: string;
|
|
58
|
+
harness: string;
|
|
59
|
+
scope: string;
|
|
60
|
+
description: string;
|
|
61
|
+
version: string | null;
|
|
62
|
+
enabled: boolean | null;
|
|
63
|
+
}
|
|
64
|
+
export interface MemoryDocumentModel {
|
|
65
|
+
key: string;
|
|
66
|
+
name: string;
|
|
67
|
+
harness: string;
|
|
68
|
+
scope: string;
|
|
69
|
+
profile: string | null;
|
|
70
|
+
size: number | null;
|
|
71
|
+
updatedAt: string | null;
|
|
72
|
+
preview: string;
|
|
73
|
+
truncated: boolean;
|
|
74
|
+
}
|
|
75
|
+
export interface MemoryMatchModel {
|
|
76
|
+
key: string;
|
|
77
|
+
name: string;
|
|
78
|
+
harness: string;
|
|
79
|
+
scope: string;
|
|
80
|
+
profile: string | null;
|
|
81
|
+
line: number | null;
|
|
82
|
+
excerpt: string;
|
|
83
|
+
}
|
|
84
|
+
export interface MemorySearchResult {
|
|
85
|
+
total: number;
|
|
86
|
+
matches: MemoryMatchModel[];
|
|
87
|
+
}
|
|
88
|
+
export interface SourceInventory {
|
|
89
|
+
source: string;
|
|
90
|
+
profiles: InventoryPage<ProfileModel>;
|
|
91
|
+
channels: InventoryPage<ChannelModel>;
|
|
92
|
+
routes: InventoryPage<RouteModel>;
|
|
93
|
+
triggers: InventoryPage<TriggerModel>;
|
|
94
|
+
skills: InventoryPage<SkillModel>;
|
|
95
|
+
memory: InventoryPage<MemoryDocumentModel>;
|
|
96
|
+
}
|
|
97
|
+
export interface ApprovalModel {
|
|
98
|
+
key: string;
|
|
99
|
+
id: string;
|
|
100
|
+
harness: string;
|
|
101
|
+
kind: string;
|
|
102
|
+
status: string;
|
|
103
|
+
subject: string;
|
|
104
|
+
session: string | null;
|
|
105
|
+
runtime: string | null;
|
|
106
|
+
ageMs: number | null;
|
|
107
|
+
options: { id: string; label: string }[];
|
|
108
|
+
}
|
|
109
|
+
export interface ApprovalResult {
|
|
110
|
+
key: string;
|
|
111
|
+
optionId: string;
|
|
112
|
+
resolved: boolean;
|
|
113
|
+
}
|
|
114
|
+
export interface AdapterModel {
|
|
115
|
+
name: string;
|
|
116
|
+
state: string;
|
|
117
|
+
failures: number | null;
|
|
118
|
+
retryAt: string | null;
|
|
119
|
+
}
|
|
120
|
+
export interface RuntimeModel {
|
|
121
|
+
source: string;
|
|
122
|
+
status: "ready" | "unavailable";
|
|
123
|
+
observedAt: string | null;
|
|
124
|
+
activeTurns: number | null;
|
|
125
|
+
leases: number | null;
|
|
126
|
+
queuedMessages: number | null;
|
|
127
|
+
pendingRequests: number | null;
|
|
128
|
+
adapters: AdapterModel[];
|
|
129
|
+
}
|
|
130
|
+
export interface ProfileListProps {
|
|
131
|
+
profiles?: ProfileModel[];
|
|
132
|
+
onOpen?(key: string, opener: HTMLButtonElement): void;
|
|
133
|
+
}
|
|
134
|
+
export interface ProfileDetailsProps {
|
|
135
|
+
profile: ProfileModel;
|
|
136
|
+
}
|
|
137
|
+
export interface ChannelListProps {
|
|
138
|
+
channels?: ChannelModel[];
|
|
139
|
+
}
|
|
140
|
+
export interface RouteListProps {
|
|
141
|
+
routes?: RouteModel[];
|
|
142
|
+
profiles?: ProfileModel[];
|
|
143
|
+
onOpenProfile?: ProfileListProps["onOpen"];
|
|
144
|
+
}
|
|
145
|
+
export interface TriggerListProps {
|
|
146
|
+
triggers?: TriggerModel[];
|
|
147
|
+
profiles?: ProfileModel[];
|
|
148
|
+
onOpenProfile?: ProfileListProps["onOpen"];
|
|
149
|
+
}
|
|
150
|
+
export interface SkillListProps {
|
|
151
|
+
skills?: SkillModel[];
|
|
152
|
+
onOpen?: ProfileListProps["onOpen"];
|
|
153
|
+
}
|
|
154
|
+
export interface SkillDetailsProps {
|
|
155
|
+
skill: SkillModel;
|
|
156
|
+
}
|
|
157
|
+
export interface MemoryBrowserProps {
|
|
158
|
+
documents?: MemoryDocumentModel[];
|
|
159
|
+
onSearch?(query: string): Promise<MemorySearchResult>;
|
|
160
|
+
}
|
|
161
|
+
export interface SourceInspectorProps {
|
|
162
|
+
inventory: SourceInventory;
|
|
163
|
+
initialSection?: SourceSection;
|
|
164
|
+
section?: SourceSection;
|
|
165
|
+
selectedKey?: string | null;
|
|
166
|
+
onNavigate?(section: SourceSection, key: string | null): void;
|
|
167
|
+
onSearchMemory?: MemoryBrowserProps["onSearch"];
|
|
168
|
+
}
|
|
169
|
+
export interface ApprovalDetailsProps {
|
|
170
|
+
approval: ApprovalModel;
|
|
171
|
+
onResolve?(key: string, optionId: string): Promise<ApprovalResult>;
|
|
172
|
+
}
|
|
173
|
+
export interface ApprovalInboxProps {
|
|
174
|
+
approvals?: ApprovalModel[];
|
|
175
|
+
status?: "ready" | "loading" | "error";
|
|
176
|
+
onResolve?: ApprovalDetailsProps["onResolve"];
|
|
177
|
+
}
|
|
178
|
+
export interface AdapterStatusProps {
|
|
179
|
+
adapters?: AdapterModel[];
|
|
180
|
+
}
|
|
181
|
+
export interface OrchestrationOverviewProps {
|
|
182
|
+
model: RuntimeModel;
|
|
183
|
+
}
|
|
184
|
+
export const SOURCE_SECTIONS: SourceSection[];
|
|
185
|
+
export function projectSourceInventory(
|
|
186
|
+
reads?: unknown,
|
|
187
|
+
options?: { sourceKey?: string; limit?: number },
|
|
188
|
+
): SourceInventory;
|
|
189
|
+
export function projectMemoryMatches(
|
|
190
|
+
read: unknown,
|
|
191
|
+
options?: { sourceKey?: string; limit?: number },
|
|
192
|
+
): MemorySearchResult;
|
|
193
|
+
export function projectApprovals(
|
|
194
|
+
read: unknown,
|
|
195
|
+
options?: { sourceKey?: string },
|
|
196
|
+
): ApprovalModel[];
|
|
197
|
+
export function createApprovalHost(options: {
|
|
198
|
+
client: {
|
|
199
|
+
listApprovals(query: any): Promise<unknown>;
|
|
200
|
+
resolveApproval(
|
|
201
|
+
id: string,
|
|
202
|
+
request: { option_id: string },
|
|
203
|
+
): Promise<{ id: string; option_id: string; resolved: boolean }>;
|
|
204
|
+
};
|
|
205
|
+
harness: string;
|
|
206
|
+
session: string;
|
|
207
|
+
sourceKey?: string;
|
|
208
|
+
allowResolve?: boolean;
|
|
209
|
+
}): {
|
|
210
|
+
list(): Promise<ApprovalModel[]>;
|
|
211
|
+
resolve(key: string, optionId: string): Promise<ApprovalResult>;
|
|
212
|
+
};
|
|
213
|
+
export function projectRuntimeState(
|
|
214
|
+
state: unknown,
|
|
215
|
+
options?: { sourceKey?: string },
|
|
216
|
+
): RuntimeModel;
|
|
217
|
+
export interface SourceInventoryHost {
|
|
218
|
+
inventory(sourceKey: string): Promise<SourceInventory>;
|
|
219
|
+
searchMemory(sourceKey: string, query: string): Promise<MemorySearchResult>;
|
|
220
|
+
}
|
|
221
|
+
/** Trusted host: configure native roots here; never accept them from browser requests. */
|
|
222
|
+
export function createSourceInventoryHost(options: {
|
|
223
|
+
client: {
|
|
224
|
+
listProfiles(query: any): Promise<unknown>;
|
|
225
|
+
listChannels(query: any): Promise<unknown>;
|
|
226
|
+
listRoutes(query: any): Promise<unknown>;
|
|
227
|
+
listTriggers(query: any): Promise<unknown>;
|
|
228
|
+
listSkills(query: any): Promise<unknown>;
|
|
229
|
+
showMemory(harness: any, query: any): Promise<unknown>;
|
|
230
|
+
searchMemory(harness: any, query: string, request: any): Promise<unknown>;
|
|
231
|
+
};
|
|
232
|
+
sources: {
|
|
233
|
+
key: string;
|
|
234
|
+
harness: string;
|
|
235
|
+
homes?: Record<string, string>;
|
|
236
|
+
profile?: string;
|
|
237
|
+
home?: string;
|
|
238
|
+
cwd?: string;
|
|
239
|
+
}[];
|
|
240
|
+
}): SourceInventoryHost;
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/** Bounded allowlisted projections. Native paths, credentials and queued prompts stay host-side. */
|
|
2
|
+
const rows = (value) => (Array.isArray(value) ? value : []);
|
|
3
|
+
const text = (value, max = 2000) =>
|
|
4
|
+
typeof value === "string"
|
|
5
|
+
? value.length > max
|
|
6
|
+
? `${value.slice(0, max - 1)}…`
|
|
7
|
+
: value
|
|
8
|
+
: "";
|
|
9
|
+
const count = (value) =>
|
|
10
|
+
Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
11
|
+
const key = (...parts) =>
|
|
12
|
+
parts.map((part) => encodeURIComponent(String(part ?? ""))).join(":");
|
|
13
|
+
const flag = (value) => (typeof value === "boolean" ? value : null);
|
|
14
|
+
const opaque = (value) => {
|
|
15
|
+
let hash = 14695981039346656037n;
|
|
16
|
+
for (const character of String(value || ""))
|
|
17
|
+
hash = BigInt.asUintN(
|
|
18
|
+
64,
|
|
19
|
+
(hash ^ BigInt(character.codePointAt(0))) * 1099511628211n,
|
|
20
|
+
);
|
|
21
|
+
return hash.toString(36);
|
|
22
|
+
};
|
|
23
|
+
export const SOURCE_SECTIONS = [
|
|
24
|
+
"profiles",
|
|
25
|
+
"channels",
|
|
26
|
+
"routes",
|
|
27
|
+
"triggers",
|
|
28
|
+
"skills",
|
|
29
|
+
"memory",
|
|
30
|
+
];
|
|
31
|
+
export function projectSourceInventory(
|
|
32
|
+
reads = {},
|
|
33
|
+
{ sourceKey = "source", limit = 100 } = {},
|
|
34
|
+
) {
|
|
35
|
+
limit = Number.isSafeInteger(limit) ? Math.min(500, Math.max(1, limit)) : 100;
|
|
36
|
+
const result = { source: sourceKey };
|
|
37
|
+
for (const section of SOURCE_SECTIONS) {
|
|
38
|
+
const read = reads[section];
|
|
39
|
+
const native = rows(
|
|
40
|
+
section === "skills"
|
|
41
|
+
? read
|
|
42
|
+
: read?.[section === "memory" ? "documents" : section],
|
|
43
|
+
);
|
|
44
|
+
const project = {
|
|
45
|
+
profiles: (r) => ({
|
|
46
|
+
key: key(sourceKey, r.harness, r.name),
|
|
47
|
+
name: text(r.name, 200),
|
|
48
|
+
harness: text(r.harness, 100),
|
|
49
|
+
kind: text(r.kind, 100),
|
|
50
|
+
isDefault: r.default === true,
|
|
51
|
+
routes: count(r.routes),
|
|
52
|
+
sessions: count(r.sessions),
|
|
53
|
+
model: text(r.model, 200) || null,
|
|
54
|
+
}),
|
|
55
|
+
channels: (r) => ({
|
|
56
|
+
key: key(sourceKey, r.harness, r.name),
|
|
57
|
+
name: text(r.name, 200),
|
|
58
|
+
harness: text(r.harness, 100),
|
|
59
|
+
kind: text(r.kind, 100),
|
|
60
|
+
account: text(r.account, 200) || null,
|
|
61
|
+
enabled: flag(r.enabled),
|
|
62
|
+
configured: flag(r.configured),
|
|
63
|
+
connection: text(r.status, 100) || "unknown",
|
|
64
|
+
sessions: count(r.sessions),
|
|
65
|
+
}),
|
|
66
|
+
routes: (r, index) => ({
|
|
67
|
+
key: key(sourceKey, r.harness, index),
|
|
68
|
+
harness: text(r.harness, 100),
|
|
69
|
+
target: text(r.target, 200),
|
|
70
|
+
profileKey: key(sourceKey, r.harness, r.target),
|
|
71
|
+
specificity: Number.isFinite(r.specificity) ? r.specificity : null,
|
|
72
|
+
isDefault: r.default === true,
|
|
73
|
+
match: Object.fromEntries(
|
|
74
|
+
[
|
|
75
|
+
"platform",
|
|
76
|
+
"account",
|
|
77
|
+
"guild",
|
|
78
|
+
"team",
|
|
79
|
+
"chat_id",
|
|
80
|
+
"peer_kind",
|
|
81
|
+
"thread_id",
|
|
82
|
+
"roles",
|
|
83
|
+
].flatMap((name) =>
|
|
84
|
+
r.match?.[name] == null
|
|
85
|
+
? []
|
|
86
|
+
: [
|
|
87
|
+
[
|
|
88
|
+
name,
|
|
89
|
+
name === "roles"
|
|
90
|
+
? rows(r.match.roles)
|
|
91
|
+
.slice(0, 30)
|
|
92
|
+
.map((v) => text(v, 200))
|
|
93
|
+
.join(", ")
|
|
94
|
+
: text(r.match[name], 200),
|
|
95
|
+
],
|
|
96
|
+
],
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
}),
|
|
100
|
+
triggers: (r) => ({
|
|
101
|
+
key: key(sourceKey, r.harness, r.name),
|
|
102
|
+
name: text(r.name, 200),
|
|
103
|
+
harness: text(r.harness, 100),
|
|
104
|
+
kind: text(r.kind, 100),
|
|
105
|
+
route: text(r.route, 500),
|
|
106
|
+
events: rows(r.events)
|
|
107
|
+
.slice(0, 30)
|
|
108
|
+
.map((v) => text(v, 200)),
|
|
109
|
+
profile: text(r.target?.profile, 200) || null,
|
|
110
|
+
profileKey: r.target?.profile
|
|
111
|
+
? key(sourceKey, r.harness, r.target.profile)
|
|
112
|
+
: null,
|
|
113
|
+
action: text(r.target?.action, 200) || null,
|
|
114
|
+
session: text(r.target?.session_key, 300) || null,
|
|
115
|
+
destination: text(r.deliver?.target, 300) || null,
|
|
116
|
+
enabled: flag(r.enabled),
|
|
117
|
+
authenticated: flag(r.authenticated),
|
|
118
|
+
description: text(r.description),
|
|
119
|
+
}),
|
|
120
|
+
skills: (r) => ({
|
|
121
|
+
key: key(sourceKey, r.harness, r.scope, r.name, opaque(r.location)),
|
|
122
|
+
name: text(r.name, 200),
|
|
123
|
+
harness: text(r.harness, 100),
|
|
124
|
+
scope: text(r.scope, 100),
|
|
125
|
+
description: text(r.description),
|
|
126
|
+
version: text(r.version, 100) || null,
|
|
127
|
+
enabled: flag(r.enabled),
|
|
128
|
+
}),
|
|
129
|
+
memory: (r) => ({
|
|
130
|
+
key: key(sourceKey, r.harness, r.scope, r.profile, r.name),
|
|
131
|
+
name: text(r.name, 300),
|
|
132
|
+
harness: text(r.harness, 100),
|
|
133
|
+
scope: text(r.scope, 100),
|
|
134
|
+
profile: text(r.profile, 200) || null,
|
|
135
|
+
size: count(r.size),
|
|
136
|
+
updatedAt: text(r.updated_at, 100) || null,
|
|
137
|
+
preview: rows(r.preview)
|
|
138
|
+
.slice(0, 30)
|
|
139
|
+
.map((v) => text(v, 500))
|
|
140
|
+
.join("\n"),
|
|
141
|
+
truncated:
|
|
142
|
+
r.truncated === true ||
|
|
143
|
+
rows(r.preview).length > 30 ||
|
|
144
|
+
rows(r.preview).some((v) => typeof v === "string" && v.length > 500),
|
|
145
|
+
}),
|
|
146
|
+
}[section];
|
|
147
|
+
result[section] = {
|
|
148
|
+
status: read === undefined ? "unavailable" : "ready",
|
|
149
|
+
items: native.slice(0, limit).map(project),
|
|
150
|
+
total: native.length,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
export function projectMemoryMatches(
|
|
156
|
+
read,
|
|
157
|
+
{ sourceKey = "source", limit = 100 } = {},
|
|
158
|
+
) {
|
|
159
|
+
const matches = rows(read?.matches);
|
|
160
|
+
const bound = Number.isSafeInteger(limit)
|
|
161
|
+
? Math.min(500, Math.max(1, limit))
|
|
162
|
+
: 100;
|
|
163
|
+
return {
|
|
164
|
+
total: matches.length,
|
|
165
|
+
matches: matches
|
|
166
|
+
.slice(0, bound)
|
|
167
|
+
.map((r, index) => ({
|
|
168
|
+
key: key(sourceKey, "match", index),
|
|
169
|
+
name: text(r.name, 300),
|
|
170
|
+
harness: text(r.harness, 100),
|
|
171
|
+
scope: text(r.scope, 100),
|
|
172
|
+
profile: text(r.profile, 200) || null,
|
|
173
|
+
line: count(r.line),
|
|
174
|
+
excerpt: text(r.excerpt, 1000),
|
|
175
|
+
})),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export function createSourceInventoryHost({ client, sources }) {
|
|
179
|
+
const byKey = new Map(sources.map((s) => [s.key, s]));
|
|
180
|
+
if (byKey.size !== sources.length)
|
|
181
|
+
throw new Error("Source keys must be unique");
|
|
182
|
+
const get = (key) => {
|
|
183
|
+
const source = byKey.get(key);
|
|
184
|
+
if (!source) throw new Error("Unknown source");
|
|
185
|
+
return source;
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
async inventory(sourceKey) {
|
|
189
|
+
const source = get(sourceKey);
|
|
190
|
+
const q = { harness: source.harness, homes: source.homes };
|
|
191
|
+
const calls = {
|
|
192
|
+
profiles: () => client.listProfiles(q),
|
|
193
|
+
channels: () => client.listChannels(q),
|
|
194
|
+
routes: () => client.listRoutes({ ...q, profile: source.profile }),
|
|
195
|
+
triggers: () => client.listTriggers(q),
|
|
196
|
+
skills: () =>
|
|
197
|
+
client.listSkills({ ...q, cwd: source.cwd || source.home }),
|
|
198
|
+
memory: () =>
|
|
199
|
+
client.showMemory(source.harness, {
|
|
200
|
+
homes: source.homes,
|
|
201
|
+
profile: source.profile,
|
|
202
|
+
cwd: source.cwd || source.home,
|
|
203
|
+
full: false,
|
|
204
|
+
}),
|
|
205
|
+
};
|
|
206
|
+
const outcomes = await Promise.allSettled(
|
|
207
|
+
SOURCE_SECTIONS.map((name) => Promise.resolve().then(calls[name])),
|
|
208
|
+
);
|
|
209
|
+
const read = Object.fromEntries(
|
|
210
|
+
outcomes.flatMap((r, i) =>
|
|
211
|
+
r.status === "fulfilled" ? [[SOURCE_SECTIONS[i], r.value]] : [],
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
const model = projectSourceInventory(read, { sourceKey });
|
|
215
|
+
outcomes.forEach((r, i) => {
|
|
216
|
+
if (r.status === "rejected") {
|
|
217
|
+
const name = SOURCE_SECTIONS[i];
|
|
218
|
+
// Exception messages can contain native paths or command lines. Keep them off this browser boundary.
|
|
219
|
+
const unsupported =
|
|
220
|
+
r.reason?.code === "unsupported_action" || r.reason?.code === -32020 ||
|
|
221
|
+
/unsupported|not supported|unavailable for/.test(
|
|
222
|
+
String(r.reason?.message),
|
|
223
|
+
);
|
|
224
|
+
model[name] = {
|
|
225
|
+
status: unsupported ? "unavailable" : "error",
|
|
226
|
+
items: [],
|
|
227
|
+
total: 0,
|
|
228
|
+
error: unsupported
|
|
229
|
+
? `This source does not support ${name}.`
|
|
230
|
+
: `Could not read ${name} from this source.`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return model;
|
|
235
|
+
},
|
|
236
|
+
async searchMemory(sourceKey, query) {
|
|
237
|
+
const source = get(sourceKey);
|
|
238
|
+
if (typeof query !== "string" || !query.trim() || query.length > 200)
|
|
239
|
+
throw new Error("Enter a search query of 1–200 characters");
|
|
240
|
+
try {
|
|
241
|
+
const read = await client.searchMemory(source.harness, query, {
|
|
242
|
+
homes: source.homes,
|
|
243
|
+
profile: source.profile,
|
|
244
|
+
cwd: source.cwd || source.home,
|
|
245
|
+
regex: false,
|
|
246
|
+
});
|
|
247
|
+
return projectMemoryMatches(read, { sourceKey });
|
|
248
|
+
} catch {
|
|
249
|
+
throw new Error("Could not search memory in this source.");
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
export function projectApprovals(read, { sourceKey = "source" } = {}) {
|
|
255
|
+
return rows(read)
|
|
256
|
+
.slice(0, 100)
|
|
257
|
+
.map((r) => ({
|
|
258
|
+
key: key(sourceKey, r.harness, r.id),
|
|
259
|
+
id: text(r.id, 300),
|
|
260
|
+
harness: text(r.harness, 100),
|
|
261
|
+
kind: text(r.kind, 100),
|
|
262
|
+
status: text(r.status, 100),
|
|
263
|
+
subject: text(r.subject),
|
|
264
|
+
session: text(r.session_id, 300) || null,
|
|
265
|
+
runtime: text(r.runtime_id, 300) || null,
|
|
266
|
+
ageMs: count(r.age_ms),
|
|
267
|
+
options:
|
|
268
|
+
r.status === "pending"
|
|
269
|
+
? rows(r.options)
|
|
270
|
+
.slice(0, 30)
|
|
271
|
+
.map((o) => ({
|
|
272
|
+
id: text(o.id, 300),
|
|
273
|
+
label: text(o.label || o.id, 300),
|
|
274
|
+
}))
|
|
275
|
+
: [],
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
/** A caller-owned live connection/session. This is not an allowlist or access-policy editor. */
|
|
279
|
+
export function createApprovalHost({
|
|
280
|
+
client,
|
|
281
|
+
harness,
|
|
282
|
+
session,
|
|
283
|
+
sourceKey = "connection",
|
|
284
|
+
allowResolve = false,
|
|
285
|
+
}) {
|
|
286
|
+
if (!harness || !session)
|
|
287
|
+
throw new Error("Approval scope requires a native harness and session");
|
|
288
|
+
let known = new Map();
|
|
289
|
+
const pending = new Set();
|
|
290
|
+
const list = async () => {
|
|
291
|
+
const native = rows(
|
|
292
|
+
await client.listApprovals({ harness, session }),
|
|
293
|
+
).filter((row) => row.harness === harness && row.session_id === session);
|
|
294
|
+
const projected = projectApprovals(native, { sourceKey });
|
|
295
|
+
known = new Map(
|
|
296
|
+
projected.map((row, index) => [row.key, { row, native: native[index] }]),
|
|
297
|
+
);
|
|
298
|
+
return projected;
|
|
299
|
+
};
|
|
300
|
+
return {
|
|
301
|
+
list,
|
|
302
|
+
async resolve(key, optionId) {
|
|
303
|
+
if (!allowResolve || !known.has(key) || pending.has(key))
|
|
304
|
+
throw new Error("Decision unavailable for this connection");
|
|
305
|
+
pending.add(key);
|
|
306
|
+
try {
|
|
307
|
+
await list();
|
|
308
|
+
const request = known.get(key);
|
|
309
|
+
if (
|
|
310
|
+
request?.row.status !== "pending" ||
|
|
311
|
+
!request.native.options.some((option) => option.id === optionId)
|
|
312
|
+
)
|
|
313
|
+
throw new Error("This request or choice is no longer available");
|
|
314
|
+
const result = await client.resolveApproval(request.native.id, {
|
|
315
|
+
option_id: optionId,
|
|
316
|
+
});
|
|
317
|
+
if (
|
|
318
|
+
result.id !== request.native.id ||
|
|
319
|
+
result.option_id !== optionId ||
|
|
320
|
+
result.resolved !== true
|
|
321
|
+
)
|
|
322
|
+
throw new Error("The source did not confirm this decision");
|
|
323
|
+
known.delete(key);
|
|
324
|
+
return { key, optionId, resolved: true };
|
|
325
|
+
} finally {
|
|
326
|
+
pending.delete(key);
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
export function projectRuntimeState(state, { sourceKey = "runtime" } = {}) {
|
|
332
|
+
if (!state)
|
|
333
|
+
return {
|
|
334
|
+
source: sourceKey,
|
|
335
|
+
status: "unavailable",
|
|
336
|
+
observedAt: null,
|
|
337
|
+
activeTurns: null,
|
|
338
|
+
leases: null,
|
|
339
|
+
queuedMessages: null,
|
|
340
|
+
pendingRequests: null,
|
|
341
|
+
adapters: [],
|
|
342
|
+
};
|
|
343
|
+
const size = (value) =>
|
|
344
|
+
value && typeof value === "object" && !Array.isArray(value)
|
|
345
|
+
? Object.keys(value).length
|
|
346
|
+
: null;
|
|
347
|
+
return {
|
|
348
|
+
source: sourceKey,
|
|
349
|
+
status: "ready",
|
|
350
|
+
observedAt: text(state.clock, 100) || null,
|
|
351
|
+
activeTurns: size(state.turns),
|
|
352
|
+
leases: size(state.leases),
|
|
353
|
+
queuedMessages:
|
|
354
|
+
size(state.queues) === null
|
|
355
|
+
? null
|
|
356
|
+
: Object.values(state.queues).reduce((n, q) => n + rows(q).length, 0),
|
|
357
|
+
pendingRequests: size(state.pending_requests),
|
|
358
|
+
adapters: Object.entries(state.adapters || {})
|
|
359
|
+
.slice(0, 100)
|
|
360
|
+
.map(([name, value]) => ({
|
|
361
|
+
name: text(name, 200),
|
|
362
|
+
state: text(value.state, 100) || "unknown",
|
|
363
|
+
failures: count(value.breaker?.failures),
|
|
364
|
+
retryAt: text(value.breaker?.until, 100) || null,
|
|
365
|
+
})),
|
|
366
|
+
};
|
|
367
|
+
}
|