@volter-ai-dev/supercode-ui 0.1.71 → 0.1.73

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.
Files changed (38) hide show
  1. package/README.md +24 -0
  2. package/activity.mjs +2 -2
  3. package/chunks/{chunk-O7Q2PELK.mjs → chunk-2FG7VKIW.mjs} +1 -1
  4. package/chunks/{chunk-XD2WJYSL.mjs → chunk-4XUMCWPC.mjs} +1 -1
  5. package/chunks/{chunk-LQMYNJPU.mjs → chunk-E65I2I2L.mjs} +1 -1
  6. package/chunks/{chunk-OW42DS6P.mjs → chunk-IL5T2AST.mjs} +4 -4
  7. package/chunks/{chunk-ATCOWFRV.mjs → chunk-TKTOVDXB.mjs} +40 -6
  8. package/chunks/{chunk-SSYNT434.mjs → chunk-WRK5TBX3.mjs} +1 -1
  9. package/components.mjs +6 -6
  10. package/embed.mjs +5 -5
  11. package/logo.mjs +1 -1
  12. package/messenger.mjs +5 -5
  13. package/package.json +21 -3
  14. package/react/activity.mjs +2 -2
  15. package/react/chunks/{chunk-ZM5X5LOF.mjs → chunk-LYRIQE5M.mjs} +1 -1
  16. package/react/chunks/{chunk-ZXD7NZXI.mjs → chunk-NJC6S2AW.mjs} +1 -1
  17. package/react/chunks/{chunk-2RIHDJT6.mjs → chunk-QL6QEYPK.mjs} +4 -4
  18. package/react/chunks/{chunk-TQHX2DQG.mjs → chunk-UGL6AB2K.mjs} +1 -1
  19. package/react/chunks/{chunk-GV5KV5UY.mjs → chunk-VFOVJ4S3.mjs} +40 -6
  20. package/react/chunks/{chunk-GYQOTTZ5.mjs → chunk-YK7SJ6EH.mjs} +1 -1
  21. package/react/components.mjs +6 -6
  22. package/react/logo.mjs +1 -1
  23. package/react/messenger.mjs +5 -5
  24. package/react/sessions.mjs +2 -2
  25. package/react/settings.mjs +2 -2
  26. package/react/subagents.mjs +2 -2
  27. package/react/supervision-components.mjs +1354 -0
  28. package/react/supervision.d.ts +36 -0
  29. package/sessions.mjs +2 -2
  30. package/settings.mjs +2 -2
  31. package/source-inventory.d.ts +240 -0
  32. package/source-inventory.mjs +367 -0
  33. package/styles.css +92 -0
  34. package/subagents.mjs +2 -2
  35. package/supervision-components.d.ts +36 -0
  36. package/supervision-components.mjs +1354 -0
  37. package/supervision.d.ts +242 -0
  38. package/supervision.mjs +349 -0
@@ -0,0 +1,36 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { ProfileListProps, ProfileDetailsProps, ChannelListProps, RouteListProps, TriggerListProps, SkillListProps, SkillDetailsProps, MemoryBrowserProps, SourceInspectorProps, ApprovalInboxProps, ApprovalDetailsProps, AdapterStatusProps, OrchestrationOverviewProps } from '../source-inventory.js';
3
+ export function ProfileList(props: ProfileListProps): ReactElement;
4
+ export function ProfileDetails(props: ProfileDetailsProps): ReactElement;
5
+ export function ChannelList(props: ChannelListProps): ReactElement;
6
+ export function RouteList(props: RouteListProps): ReactElement;
7
+ export function TriggerList(props: TriggerListProps): ReactElement;
8
+ export function SkillList(props: SkillListProps): ReactElement;
9
+ export function SkillDetails(props: SkillDetailsProps): ReactElement;
10
+ export function MemoryBrowser(props: MemoryBrowserProps): ReactElement;
11
+ export function SourceInspector(props: SourceInspectorProps): ReactElement;
12
+ export function ApprovalInbox(props: ApprovalInboxProps): ReactElement;
13
+ export function ApprovalDetails(props: ApprovalDetailsProps): ReactElement;
14
+ export function AdapterStatus(props: AdapterStatusProps): ReactElement;
15
+ export function OrchestrationOverview(props: OrchestrationOverviewProps): ReactElement;
16
+ export type * from '../supervision.js';
17
+ import type { WorkflowBoardProps, WorkflowListProps, TaskCardProps, TaskDetailsProps, DependencyListProps, AttemptTimelineProps, HandoffCardProps, ReviewCardProps, TaskThreadProps, JobListProps, JobDetailsProps, JobActionsProps, RunListProps, RunDetailsProps, DeliveryStatusProps, OrchestrationJobsProps } from '../supervision.js';
18
+ export function WorkflowBoard(props: WorkflowBoardProps): ReactElement;
19
+ export function WorkflowList(props: WorkflowListProps): ReactElement;
20
+ export function TaskCard(props: TaskCardProps): ReactElement;
21
+ export function TaskDetails(props: TaskDetailsProps): ReactElement;
22
+ export function DependencyList(props: DependencyListProps): ReactElement;
23
+ export function AttemptTimeline(props: AttemptTimelineProps): ReactElement;
24
+ export function HandoffCard(props: HandoffCardProps): ReactElement;
25
+ export function ReviewCard(props: ReviewCardProps): ReactElement;
26
+ export function TaskThread(props: TaskThreadProps): ReactElement;
27
+ export function JobList(props: JobListProps): ReactElement;
28
+ export function JobDetails(props: JobDetailsProps): ReactElement;
29
+ export function JobActions(props: JobActionsProps): ReactElement;
30
+ export function RunList(props: RunListProps): ReactElement;
31
+ export function RunDetails(props: RunDetailsProps): ReactElement;
32
+ export function DeliveryStatus(props: DeliveryStatusProps): ReactElement;
33
+ export function OrchestrationJobs(props: OrchestrationJobsProps): ReactElement;
34
+
35
+ import type { JobControlsProps } from '../supervision.js';
36
+ export function JobControls(props: JobControlsProps): ReactElement;
package/sessions.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  SessionList,
3
3
  SessionRow
4
- } from "./chunks/chunk-O7Q2PELK.mjs";
5
- import "./chunks/chunk-ATCOWFRV.mjs";
4
+ } from "./chunks/chunk-2FG7VKIW.mjs";
5
+ import "./chunks/chunk-TKTOVDXB.mjs";
6
6
  import "./chunks/chunk-LOP6XOON.mjs";
7
7
  import "./chunks/chunk-V3UM7H7W.mjs";
8
8
  import "./chunks/chunk-LS6JBXNT.mjs";
package/settings.mjs CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  HarnessPicker,
4
4
  HarnessReadiness,
5
5
  HarnessSettingsPanel
6
- } from "./chunks/chunk-SSYNT434.mjs";
7
- import "./chunks/chunk-ATCOWFRV.mjs";
6
+ } from "./chunks/chunk-WRK5TBX3.mjs";
7
+ import "./chunks/chunk-TKTOVDXB.mjs";
8
8
  import "./chunks/chunk-LS6JBXNT.mjs";
9
9
  import "./chunks/chunk-ZLYHUYE2.mjs";
10
10
  export {
@@ -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
+ }