@agimon-ai/po-help 0.0.0-alpha.105 → 0.0.0-alpha.107

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.
@@ -63,6 +63,145 @@ var root_server_default$1 = (0, _agimon_ai_po_core_extensionFile.defineRoot)((co
63
63
  services: context.host.scope === "workspace" ? [brainSimulationProvider] : []
64
64
  }));
65
65
  //#endregion
66
+ //#region src/types/brainGraph.ts
67
+ const BRAIN_GRAPH_KINDS = [
68
+ "backend",
69
+ "web",
70
+ "native"
71
+ ];
72
+ const CODEMODE_SERVICE = "po/codemode";
73
+ const PLUMBING_SERVICES = /* @__PURE__ */ new Set([
74
+ "po/server-scope",
75
+ "po/server-host",
76
+ "po/headless-host",
77
+ "po/global-services"
78
+ ]);
79
+ function graphOf(nodes, links) {
80
+ const linked = new Set([...links.values()].flatMap((link) => [link.source, link.target]));
81
+ return {
82
+ nodes: [...nodes.values()].filter((node) => linked.has(node.id)).sort((a, b) => a.id.localeCompare(b.id)),
83
+ links: [...links.values()].sort((a, b) => `${a.source}\0${a.target}`.localeCompare(`${b.source}\0${b.target}`))
84
+ };
85
+ }
86
+ /**
87
+ * Public services, services a code-mode contributor can call (every service a plugin that injects
88
+ * po/codemode resolves), and the plugins that consume them. Nodes collapse by name across scopes.
89
+ * A nested fiber is an intermediary, so its injections fold into the plugin that owns it; unowned
90
+ * fibers, unresolved injections and scope plumbing are left out.
91
+ */
92
+ function backendGraph(snapshot) {
93
+ const plugins = new Map(snapshot.consumers.flatMap((consumer) => consumer.owner.kind === "package" && consumer.owner.packageName ? [[consumer.id, {
94
+ name: consumer.owner.packageName,
95
+ origin: consumer.owner.origin
96
+ }]] : []));
97
+ const services = new Map(snapshot.services.map((service) => [service.id, service]));
98
+ const uses = snapshot.edges.flatMap((edge) => {
99
+ const plugin = plugins.get(edge.consumer);
100
+ const service = edge.service === null ? void 0 : services.get(edge.service);
101
+ return plugin && service && !PLUMBING_SERVICES.has(service.name) ? [{
102
+ plugin,
103
+ service
104
+ }] : [];
105
+ });
106
+ const contributors = new Set(uses.filter((use) => use.service.name === CODEMODE_SERVICE).map((use) => use.plugin.name));
107
+ const reachable = new Set(uses.filter((use) => contributors.has(use.plugin.name)).map((use) => use.service.name));
108
+ const nodes = /* @__PURE__ */ new Map();
109
+ const links = /* @__PURE__ */ new Map();
110
+ for (const { plugin, service } of uses) {
111
+ if (!service.public && !reachable.has(service.name)) continue;
112
+ const serviceId = `service:${service.name}`;
113
+ const previous = nodes.get(serviceId);
114
+ if (!previous || service.description && previous.detail.endsWith("No description.")) nodes.set(serviceId, {
115
+ id: serviceId,
116
+ label: service.name,
117
+ role: reachable.has(service.name) ? "codemode" : "public",
118
+ detail: `${service.public ? "Public" : "Private"} service${reachable.has(service.name) ? ", callable from code mode" : ""}. Provided by ${service.owner.packageName ?? service.owner.kind}. ${service.description ?? "No description."}`
119
+ });
120
+ const pluginId = `plugin:${plugin.name}`;
121
+ nodes.set(pluginId, {
122
+ id: pluginId,
123
+ label: plugin.name,
124
+ role: plugin.origin === "personal" ? "personal" : "plugin",
125
+ detail: `Plugin${contributors.has(plugin.name) ? " that contributes to code mode" : ""}.`
126
+ });
127
+ links.set(`${pluginId}\0${serviceId}`, {
128
+ source: pluginId,
129
+ target: serviceId,
130
+ label: "consumes"
131
+ });
132
+ }
133
+ return graphOf(nodes, links);
134
+ }
135
+ /** Each plugin and the slots it fills or declares, for one client host. */
136
+ function clientGraph(packages, target, hostSlots = {}) {
137
+ const hostDescriptions = new Map(Object.values(hostSlots).map((slot) => [slot.slot, slot.description]));
138
+ const nodes = /* @__PURE__ */ new Map();
139
+ const links = /* @__PURE__ */ new Map();
140
+ const slot = (name, description) => {
141
+ const id = `slot:${name}`;
142
+ if (!nodes.has(id) || description) nodes.set(id, {
143
+ id,
144
+ label: name,
145
+ role: "slot",
146
+ detail: `Slot. ${description ?? hostDescriptions.get(name) ?? "No description."}`
147
+ });
148
+ return id;
149
+ };
150
+ for (const pkg of packages) {
151
+ const metadata = target === "web" ? pkg.web : pkg.native;
152
+ if (!metadata || !pkg.owner.packageName) continue;
153
+ const pluginId = `plugin:${pkg.owner.packageName}`;
154
+ nodes.set(pluginId, {
155
+ id: pluginId,
156
+ label: pkg.owner.packageName,
157
+ role: pkg.owner.origin === "personal" ? "personal" : "plugin",
158
+ detail: `Plugin with a ${target} half.`
159
+ });
160
+ for (const declared of metadata.slots) {
161
+ const slotId = slot(declared.name, declared.description);
162
+ links.set(`${pluginId}\0${slotId}`, {
163
+ source: pluginId,
164
+ target: slotId,
165
+ label: "declares"
166
+ });
167
+ }
168
+ for (const fill of metadata.fills) {
169
+ if (fill.slot === null) continue;
170
+ const slotId = slot(fill.slot);
171
+ links.set(`${pluginId}\0${slotId}`, {
172
+ source: pluginId,
173
+ target: slotId,
174
+ label: "fills"
175
+ });
176
+ }
177
+ }
178
+ return graphOf(nodes, links);
179
+ }
180
+ /** One essential graph. Web prefers the exact browser mount inventory; native reads installed packages. */
181
+ function brainGraph(snapshot, kind, hostSlots) {
182
+ if (kind === "backend") return backendGraph(snapshot);
183
+ return clientGraph(kind === "web" ? snapshot.clientPackages ?? snapshot.packages : snapshot.packages, kind, hostSlots);
184
+ }
185
+ /** A node and its direct connections in one graph, or undefined when the graph lacks it. */
186
+ function brainNodeView(graph, kind, id) {
187
+ const byId = new Map(graph.nodes.map((node) => [node.id, node]));
188
+ const node = byId.get(id);
189
+ if (!node) return void 0;
190
+ return {
191
+ kind,
192
+ node,
193
+ links: graph.links.flatMap((link) => link.source === id ? [{
194
+ label: link.label,
195
+ direction: "out",
196
+ node: byId.get(link.target)
197
+ }] : link.target === id ? [{
198
+ label: link.label,
199
+ direction: "in",
200
+ node: byId.get(link.source)
201
+ }] : [])
202
+ };
203
+ }
204
+ //#endregion
66
205
  //#region src/services/brain/index.ts
67
206
  function createBrain(options) {
68
207
  const services = (signal) => {
@@ -154,14 +293,39 @@ function createBrain(options) {
154
293
  const DECLARATIONS$1 = `type Scope = { id: string; level: 'global' | 'workspace' | 'session' };
155
294
  type Owner = { kind: 'host' | 'package' | 'unknown'; packageName: string | null; origin: 'po' | 'personal' | 'unknown' };
156
295
  interface WebMetadata { version: 1; slots: { name: string; scope: Scope['level']; description?: string; parent?: string; kind: 'data' | 'component' }[]; fills: { id: string; slot: string | null; scope: Scope['level']; placement: 'verified' | 'unverified' }[]; diagnostics: string[] }
157
- interface PackageMetadata { scope: Scope; owner: Owner; web: WebMetadata | null }
296
+ interface PackageMetadata { scope: Scope; owner: Owner; web: WebMetadata | null; native?: WebMetadata | null }
158
297
  interface ServicesSnapshot { scope: Scope; scopes: Scope[]; packages: PackageMetadata[]; clientPackages?: PackageMetadata[]; services: { id: string; name: string; scope: Scope; visibleFrom: Scope; owner: Owner; public: boolean; description: string | null }[]; consumers: { id: string; name: string; scope: Scope; owner: Owner }[]; edges: { consumer: string; service: string | null; name: string; status: 'resolved' | 'waiting' }[]; diagnostics: string[] }
159
298
  interface SlotsSnapshot { scope: Scope; scopes: Scope[]; slots: { id: string; name: string; scope: Scope; owner: Owner; description: string | null; parent: string | null; kind: 'data' | 'component' }[]; fills: { id: string; slot: string | null; scope: Scope; owner: Owner; placement: 'verified' | 'unverified' }[]; diagnostics: string[] }
160
- declare const brain: { services(): Promise<ServicesSnapshot>; slots(): Promise<SlotsSnapshot> };`;
299
+ type GraphKind = 'backend' | 'web' | 'native';
300
+ type Role = 'public' | 'codemode' | 'plugin' | 'personal' | 'slot';
301
+ interface GraphNode { id: string; label: string; role: Role; detail: string }
302
+ /** backend: plugin -consumes-> service. web/native: plugin -fills|declares-> slot. */
303
+ interface Graph { kind: GraphKind; nodes: GraphNode[]; links: { source: string; target: string; label: string }[] }
304
+ interface NodeView { kind: GraphKind; node: GraphNode; links: { label: string; direction: 'out' | 'in'; node: GraphNode }[] }
305
+ declare const brain: {
306
+ /** The essential map the Brain page draws. Ids look like 'plugin:@scope/name', 'service:po/fs', 'slot:page'. */
307
+ graph(input?: { kind?: GraphKind }): Promise<Graph>;
308
+ /** One node and its direct links in every graph that has it, or only in kind. Empty when absent. */
309
+ node(input: { id: string; kind?: GraphKind }): Promise<NodeView[]>;
310
+ /** Raw snapshot with fibers, private services, waiting injections and diagnostics. */
311
+ services(): Promise<ServicesSnapshot>;
312
+ /** Raw web slot declarations, parent links and fill placements. */
313
+ slots(): Promise<SlotsSnapshot>;
314
+ };`;
315
+ function graphKind(value) {
316
+ if (value === void 0) return "backend";
317
+ if (BRAIN_GRAPH_KINDS.includes(value)) return value;
318
+ throw new Error(`kind must be one of ${BRAIN_GRAPH_KINDS.join(", ")}.`);
319
+ }
320
+ function input(value) {
321
+ if (value === void 0) return {};
322
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) return value;
323
+ throw new Error("Expected an options object.");
324
+ }
161
325
  function createBrainNamespace(options) {
162
326
  return {
163
327
  name: "brain",
164
- summary: "Inspect scoped service ownership, declared dependencies and slot metadata. Read-only, Help mode.",
328
+ summary: "Explore the essential service and slot graphs (backend, web, native) and raw service and slot snapshots. Read-only, Help mode.",
165
329
  declarations: DECLARATIONS$1,
166
330
  bind(run) {
167
331
  if (run.sessionId !== options.sessionId) throw new Error("The brain namespace belongs to another session.");
@@ -170,6 +334,24 @@ function createBrainNamespace(options) {
170
334
  options.assertActive();
171
335
  };
172
336
  return {
337
+ graph: async (options_) => {
338
+ check();
339
+ const kind = graphKind(input(options_).kind);
340
+ return {
341
+ kind,
342
+ ...brainGraph(options.service.services(run.signal), kind, _agimon_ai_po_core_serverPlugin.HOST_SLOT_METADATA)
343
+ };
344
+ },
345
+ node: async (options_) => {
346
+ check();
347
+ const { id, kind } = input(options_);
348
+ if (typeof id !== "string" || !id.trim() || id.length > 1024) throw new Error("id must be a node id string.");
349
+ const snapshot = options.service.services(run.signal);
350
+ return (kind === void 0 ? BRAIN_GRAPH_KINDS : [graphKind(kind)]).flatMap((each) => {
351
+ const view = brainNodeView(brainGraph(snapshot, each, _agimon_ai_po_core_serverPlugin.HOST_SLOT_METADATA), each, id);
352
+ return view ? [view] : [];
353
+ });
354
+ },
173
355
  services: async () => {
174
356
  check();
175
357
  return options.service.services(run.signal);