@agimon-ai/po-author 0.0.0-alpha.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/apiContracts.cjs +348 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +343 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/authorFacade--Lc599Ft.mjs +84 -0
  10. package/dist/authorFacade--Lc599Ft.mjs.map +1 -0
  11. package/dist/authorFacade-89Eb2-sW.d.mts +132 -0
  12. package/dist/authorFacade-89Eb2-sW.d.mts.map +1 -0
  13. package/dist/authorFacade-BYvlm8Ff.cjs +119 -0
  14. package/dist/authorFacade-BYvlm8Ff.cjs.map +1 -0
  15. package/dist/authorFacade.cjs +27 -0
  16. package/dist/authorFacade.d.mts +3 -0
  17. package/dist/authorFacade.mjs +3 -0
  18. package/dist/extensions/server.cjs +845 -0
  19. package/dist/extensions/server.cjs.map +1 -0
  20. package/dist/extensions/server.d.mts +5 -0
  21. package/dist/extensions/server.d.mts.map +1 -0
  22. package/dist/extensions/server.mjs +838 -0
  23. package/dist/extensions/server.mjs.map +1 -0
  24. package/dist/extensions/web.mjs +4758 -0
  25. package/dist/extensions/web.mjs.map +1 -0
  26. package/dist/index.cjs +101 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.mts +435 -0
  29. package/dist/index.d.mts.map +1 -0
  30. package/dist/index.mjs +70 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/structuredDocuments-B914GLxx.mjs +1064 -0
  33. package/dist/structuredDocuments-B914GLxx.mjs.map +1 -0
  34. package/dist/structuredDocuments-CNac1JmJ.cjs +1316 -0
  35. package/dist/structuredDocuments-CNac1JmJ.cjs.map +1 -0
  36. package/llms.txt +11 -0
  37. package/package.json +140 -0
  38. package/src/prompts/po-use-author/SKILL.md +20 -0
  39. package/src/types/author.ts +61 -0
  40. package/src/types/authorPreview.ts +98 -0
  41. package/src/types/structuredDocuments.ts +63 -0
  42. package/src/types/webAuthor.ts +94 -0
@@ -0,0 +1,845 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ const require_structuredDocuments = require("../structuredDocuments-CNac1JmJ.cjs");
6
+ const require_authorFacade = require("../authorFacade-BYvlm8Ff.cjs");
7
+ let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
8
+ let _agimon_ai_po_minor_mode = require("@agimon-ai/po-minor-mode");
9
+ let node_crypto = require("node:crypto");
10
+ let node_path = require("node:path");
11
+ node_path = require_structuredDocuments.__toESM(node_path, 1);
12
+ let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
13
+ let hono = require("hono");
14
+ let node_fs_promises = require("node:fs/promises");
15
+ node_fs_promises = require_structuredDocuments.__toESM(node_fs_promises, 1);
16
+ let _agimon_ai_po_core_web = require("@agimon-ai/po-core/web");
17
+ //#region src/services/structuredDocuments/document.ts
18
+ const AUTHOR_DOCUMENT_MAX_BYTES = 26214400;
19
+ async function readDocument(cwd, requestedPath) {
20
+ if (typeof requestedPath !== "string" || requestedPath.length === 0 || requestedPath.includes("\0") || node_path.default.isAbsolute(requestedPath)) throw new Error("A relative document path is required.");
21
+ const root = await node_fs_promises.default.realpath(cwd);
22
+ const candidate = node_path.default.resolve(root, requestedPath);
23
+ if (candidate !== root && !candidate.startsWith(`${root}${node_path.default.sep}`)) throw new require_structuredDocuments.DocumentRequestError("Document path is outside the working directory.", 403);
24
+ const realCandidate = await node_fs_promises.default.realpath(candidate);
25
+ if (realCandidate !== root && !realCandidate.startsWith(`${root}${node_path.default.sep}`)) throw new require_structuredDocuments.DocumentRequestError("Document path is outside the working directory.", 403);
26
+ if ((await node_fs_promises.default.stat(realCandidate)).size > 26214400) throw require_structuredDocuments.documentTooLarge(`Document exceeds the ${AUTHOR_DOCUMENT_MAX_BYTES} byte preview limit.`);
27
+ return await node_fs_promises.default.readFile(realCandidate);
28
+ }
29
+ //#endregion
30
+ //#region src/services/authorCanvasRegistry/index.ts
31
+ const MAX_CANVASES = 32;
32
+ function createAuthorCanvasRegistry(cwd, createBridge) {
33
+ const byAlias = /* @__PURE__ */ new Map();
34
+ const byPath = /* @__PURE__ */ new Map();
35
+ const select = (alias) => {
36
+ if (alias !== void 0) {
37
+ if (!require_structuredDocuments.AUTHOR_ALIAS_PATTERN.test(alias)) throw new require_structuredDocuments.AuthorBridgeError("Invalid Author canvas alias.", 400);
38
+ const canvas = byAlias.get(alias);
39
+ if (canvas === void 0) throw new require_structuredDocuments.AuthorBridgeError(`Unknown Author canvas '${alias}'. Call describe_author_tools({}) for available aliases.`, 404);
40
+ return canvas;
41
+ }
42
+ const canvases = [...byAlias.values()].filter((canvas) => !canvas.closed);
43
+ if (canvases.length !== 1) throw new require_structuredDocuments.AuthorBridgeError("Specify a canvas alias. Call describe_author_tools({}) to list canvases.", 409);
44
+ return canvases[0];
45
+ };
46
+ const view = (canvas) => {
47
+ if (canvas.closed) return {
48
+ alias: canvas.alias,
49
+ path: canvas.path,
50
+ status: "unavailable"
51
+ };
52
+ try {
53
+ canvas.bridge.describe();
54
+ canvas.readyOnce = true;
55
+ return {
56
+ alias: canvas.alias,
57
+ path: canvas.path,
58
+ status: "ready"
59
+ };
60
+ } catch {
61
+ return {
62
+ alias: canvas.alias,
63
+ path: canvas.path,
64
+ status: canvas.readyOnce ? "unavailable" : "opening"
65
+ };
66
+ }
67
+ };
68
+ return {
69
+ async open(requestedPath, signal, requestedAlias) {
70
+ signal?.throwIfAborted();
71
+ if (requestedAlias !== void 0 && !require_structuredDocuments.AUTHOR_ALIAS_PATTERN.test(requestedAlias)) throw new require_structuredDocuments.AuthorBridgeError("Invalid Author canvas alias.", 400);
72
+ const bytes = await readDocument(cwd, requestedPath);
73
+ signal?.throwIfAborted();
74
+ const root = await node_fs_promises.default.realpath(cwd);
75
+ const realPath = await node_fs_promises.default.realpath(node_path.default.resolve(root, requestedPath));
76
+ if (realPath !== root && !realPath.startsWith(`${root}${node_path.default.sep}`)) throw new require_structuredDocuments.AuthorBridgeError("Document path is outside the working directory.", 400);
77
+ const canonicalPath = node_path.default.relative(root, realPath).split(node_path.default.sep).join("/");
78
+ const occupied = requestedAlias === void 0 ? void 0 : byAlias.get(requestedAlias);
79
+ if (occupied !== void 0 && occupied.realPath !== realPath) throw new require_structuredDocuments.AuthorBridgeError(`Author canvas alias '${requestedAlias}' already names ${occupied.path}.`, 409);
80
+ const existing = byPath.get(realPath);
81
+ if (existing !== void 0) {
82
+ if (existing.closed) existing.readyOnce = false;
83
+ existing.closed = false;
84
+ return {
85
+ path: existing.path,
86
+ byteLength: bytes.byteLength,
87
+ alias: existing.alias,
88
+ status: view(existing).status,
89
+ reused: true
90
+ };
91
+ }
92
+ if (byAlias.size >= MAX_CANVASES) throw new require_structuredDocuments.AuthorBridgeError("Too many Author canvases in this conversation.", 409);
93
+ const base = node_path.default.basename(canonicalPath).replace(/\.[^.]*$/u, "").toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^[^a-z]+/u, "canvas-").slice(0, 48) || "canvas";
94
+ let alias = requestedAlias ?? base;
95
+ for (let suffix = 2; byAlias.has(alias); suffix++) alias = `${base.slice(0, 56)}-${suffix}`;
96
+ const canvas = {
97
+ alias,
98
+ path: canonicalPath,
99
+ realPath,
100
+ bridge: createBridge(),
101
+ closed: false,
102
+ readyOnce: false
103
+ };
104
+ byAlias.set(alias, canvas);
105
+ byPath.set(realPath, canvas);
106
+ return {
107
+ path: canonicalPath,
108
+ byteLength: bytes.byteLength,
109
+ alias,
110
+ status: "opening",
111
+ reused: false
112
+ };
113
+ },
114
+ describe(alias) {
115
+ const canvases = [...byAlias.values()].map(view);
116
+ const target = alias === void 0 && canvases.filter((canvas) => canvas.status !== "unavailable").length !== 1 ? void 0 : select(alias);
117
+ if (target === void 0) return {
118
+ catalogToken: "",
119
+ tools: [],
120
+ canvases,
121
+ nextStep: "Open a file with open_authoring_file, then describe_author_tools({\"alias\":\"canvas-name\"})."
122
+ };
123
+ const status = view(target).status;
124
+ const base = {
125
+ alias: target.alias,
126
+ path: target.path,
127
+ status,
128
+ canvases
129
+ };
130
+ if (status !== "ready") return {
131
+ ...base,
132
+ catalogToken: "",
133
+ tools: [],
134
+ nextStep: status === "opening" ? `Wait for ${target.alias} to be ready, then call describe_author_tools({"alias":"${target.alias}"}).` : `Reopen ${target.path} with open_authoring_file({"path":"${target.path}","alias":"${target.alias}"}).`
135
+ };
136
+ return {
137
+ ...base,
138
+ ...target.bridge.describe()
139
+ };
140
+ },
141
+ invoke(input, signal) {
142
+ const target = select(input.alias);
143
+ if (target.closed) throw new require_structuredDocuments.AuthorBridgeError(`Canvas '${target.alias}' is closed. Reopen its file.`, 503);
144
+ return target.bridge.invoke(input, signal).then((result) => ({
145
+ ...result,
146
+ alias: target.alias
147
+ }));
148
+ },
149
+ bridge(alias) {
150
+ const canvas = select(alias);
151
+ if (canvas.closed) throw new require_structuredDocuments.AuthorBridgeError(`Canvas '${alias}' is closed. Reopen its file.`, 503);
152
+ return canvas.bridge;
153
+ },
154
+ close(alias) {
155
+ const canvas = select(alias);
156
+ canvas.closed = true;
157
+ canvas.bridge.close();
158
+ canvas.bridge = createBridge();
159
+ },
160
+ closeAll() {
161
+ for (const canvas of byAlias.values()) {
162
+ if (canvas.closed) continue;
163
+ canvas.closed = true;
164
+ canvas.bridge.close();
165
+ canvas.bridge = createBridge();
166
+ }
167
+ },
168
+ dispose() {
169
+ for (const canvas of byAlias.values()) canvas.bridge.close();
170
+ byAlias.clear();
171
+ byPath.clear();
172
+ }
173
+ };
174
+ }
175
+ //#endregion
176
+ //#region src/types/apiRoutes.ts
177
+ /**
178
+ * This package's routes, as data.
179
+ *
180
+ * No scope and no base path. The build reads both off
181
+ * `src/extensions/workspaces/sessions/(backend)/api/author/`, so the mount is
182
+ * stated once, by the folder that creates it. The browser used to restate it:
183
+ * `authorStateUrl` built one URL from `API_BASE_PATH` and the document helper
184
+ * two files away spelled `author` out again as a literal, so the two could
185
+ * disagree and nothing would say so.
186
+ *
187
+ * The order below is the order the three Hono apps register in: `/state` in
188
+ * `authorApi`, the eight bridge routes in `authorBridgeApi`, the three document
189
+ * routes in `authorDocumentApi`. A reviewer can read the two side by side.
190
+ *
191
+ * Response types are named only where a caller reads the body. The bridge
192
+ * routes are called by the hub's author channel over `requestSessionApi`, which
193
+ * has its own message vocabulary and never sees this client.
194
+ */
195
+ var apiRoutes_default = (0, _agimon_ai_po_core_web.defineApiRoutes)({
196
+ state: {
197
+ method: "GET",
198
+ path: "/state",
199
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
200
+ },
201
+ bridgeRegister: {
202
+ method: "POST",
203
+ path: "/bridge/register"
204
+ },
205
+ bridgeCatalog: {
206
+ method: "POST",
207
+ path: "/bridge/catalog"
208
+ },
209
+ /** Long-polls for the next capability request, so it settles when one arrives. */
210
+ bridgeNext: {
211
+ method: "POST",
212
+ path: "/bridge/next"
213
+ },
214
+ bridgeResult: {
215
+ method: "POST",
216
+ path: "/bridge/result"
217
+ },
218
+ bridgeCancelled: {
219
+ method: "POST",
220
+ path: "/bridge/cancelled"
221
+ },
222
+ bridgeDisconnect: {
223
+ method: "POST",
224
+ path: "/bridge/disconnect"
225
+ },
226
+ bridgeClose: {
227
+ method: "POST",
228
+ path: "/bridge/close"
229
+ },
230
+ bridgeDescribe: {
231
+ method: "GET",
232
+ path: "/bridge/describe",
233
+ query: ["alias"]
234
+ },
235
+ bridgeInvoke: {
236
+ method: "POST",
237
+ path: "/bridge/invoke"
238
+ },
239
+ /**
240
+ * Parses one structured document.
241
+ *
242
+ * Answers a byte-length summary instead when the request names no format,
243
+ * which is the agent-side tool path; the page always names one.
244
+ */
245
+ documentsOpen: {
246
+ method: "POST",
247
+ path: "/documents/open",
248
+ query: ["session"],
249
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
250
+ },
251
+ documentsPreflight: {
252
+ method: "POST",
253
+ path: "/documents/preflight",
254
+ query: ["session"],
255
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
256
+ },
257
+ documentsSerialize: {
258
+ method: "POST",
259
+ path: "/documents/serialize",
260
+ query: ["session"],
261
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
262
+ },
263
+ /**
264
+ * The host's own bytes route, not this package's.
265
+ *
266
+ * `host: true` hangs it off the session root rather than under `/plugins`,
267
+ * which is how the cockpit already serves it. Author reads a document's
268
+ * bytes and its digest through it, and writes the edited bytes back to the
269
+ * same path, so both stay raw transfers rather than calls: the body is the
270
+ * file and the digest travels in a header.
271
+ */
272
+ file: {
273
+ method: "GET",
274
+ path: "/file",
275
+ query: ["path"],
276
+ host: true
277
+ }
278
+ });
279
+ //#endregion
280
+ //#region src/types/authorApi.ts
281
+ /**
282
+ * The wire vocabulary this package's API shares with whatever calls it.
283
+ *
284
+ * Paths live in `./apiRoutes`, which the Hono apps register from and the
285
+ * generated client addresses, so neither half can move alone. What stays here
286
+ * is the mount segment the server registers under and the shape the state
287
+ * route answers with.
288
+ */
289
+ /** Where a host mounts this package's API; the segment after /api/plugins/. */
290
+ const API_BASE_PATH = "author";
291
+ //#endregion
292
+ //#region src/extensions/workspaces/sessions/(backend)/api/author/_lib/authorBridgeApi.ts
293
+ function record(value) {
294
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new require_structuredDocuments.AuthorBridgeError("Invalid request.", 400);
295
+ return value;
296
+ }
297
+ function text(value, key) {
298
+ const result = record(value)[key];
299
+ if (typeof result !== "string" || result === "") throw new require_structuredDocuments.AuthorBridgeError(`Invalid ${key}.`, 400);
300
+ return result;
301
+ }
302
+ function generation(value) {
303
+ const result = record(value).generation;
304
+ if (!Number.isSafeInteger(result) || result < 0) throw new require_structuredDocuments.AuthorBridgeError("Invalid generation.", 400);
305
+ return result;
306
+ }
307
+ async function body(request) {
308
+ return record(await request.json());
309
+ }
310
+ function createAuthorBridgeApi(registry) {
311
+ const app = new hono.Hono();
312
+ app.onError((error, context) => context.json({ error: error instanceof Error ? error.message : String(error) }, error instanceof require_structuredDocuments.AuthorBridgeError ? error.status : 500));
313
+ app.post(apiRoutes_default.bridgeRegister.path, async (context) => {
314
+ const value = await body(context.req.raw);
315
+ const alias = text(value, "alias");
316
+ return context.json({
317
+ ...registry.bridge(alias).register(text(value, "bindingId"), generation(value)),
318
+ alias
319
+ });
320
+ });
321
+ app.post(apiRoutes_default.bridgeCatalog.path, async (context) => {
322
+ const value = await body(context.req.raw);
323
+ const tools = value.tools;
324
+ if (!Array.isArray(tools)) throw new require_structuredDocuments.AuthorBridgeError("Invalid Author catalog.", 400);
325
+ const alias = text(value, "alias");
326
+ return context.json({
327
+ ...registry.bridge(alias).catalog(text(value, "bindingId"), generation(value), text(value, "ownerToken"), tools),
328
+ alias
329
+ });
330
+ });
331
+ app.post(apiRoutes_default.bridgeNext.path, async (context) => {
332
+ const value = await body(context.req.raw);
333
+ const alias = text(value, "alias");
334
+ return context.json({
335
+ ...await registry.bridge(alias).next(text(value, "bindingId"), generation(value), text(value, "ownerToken"), context.req.raw.signal),
336
+ alias
337
+ });
338
+ });
339
+ app.post(apiRoutes_default.bridgeResult.path, async (context) => {
340
+ const value = await body(context.req.raw);
341
+ registry.bridge(text(value, "alias")).result(text(value, "bindingId"), generation(value), text(value, "ownerToken"), text(value, "catalogToken"), text(value, "requestId"), value.result);
342
+ return context.json({ accepted: true });
343
+ });
344
+ app.post(apiRoutes_default.bridgeCancelled.path, async (context) => {
345
+ const value = await body(context.req.raw);
346
+ registry.bridge(text(value, "alias")).cancelled(text(value, "bindingId"), generation(value), text(value, "ownerToken"), text(value, "catalogToken"), text(value, "requestId"));
347
+ return context.json({ accepted: true });
348
+ });
349
+ app.post(apiRoutes_default.bridgeDisconnect.path, async (context) => {
350
+ const value = await body(context.req.raw);
351
+ registry.bridge(text(value, "alias")).disconnect(text(value, "bindingId"), generation(value));
352
+ return context.json({ accepted: true });
353
+ });
354
+ app.post(apiRoutes_default.bridgeClose.path, async (context) => {
355
+ const value = await body(context.req.raw);
356
+ registry.close(text(value, "alias"));
357
+ return context.json({ accepted: true });
358
+ });
359
+ app.get(apiRoutes_default.bridgeDescribe.path, (context) => context.json(registry.describe(context.req.query("alias"))));
360
+ app.post(apiRoutes_default.bridgeInvoke.path, async (context) => {
361
+ const value = require_structuredDocuments.parseUseAuthorToolInput(await context.req.raw.json());
362
+ return context.json(await registry.invoke(value, context.req.raw.signal));
363
+ });
364
+ return app;
365
+ }
366
+ //#endregion
367
+ //#region src/extensions/workspaces/sessions/(backend)/api/author/_lib/authorDocumentApi.ts
368
+ const FORMATS = /* @__PURE__ */ new Set([
369
+ "markdown-slides",
370
+ "csv",
371
+ "pptx",
372
+ "xlsx"
373
+ ]);
374
+ function errorResponse(context, error) {
375
+ const message = error instanceof Error ? error.message : "Document operation failed.";
376
+ return context.json({ error: message }, error instanceof require_structuredDocuments.DocumentRequestError ? error.status : 422);
377
+ }
378
+ function requestFormat(value) {
379
+ if (typeof value !== "string" || !FORMATS.has(value)) throw new Error("A supported format is required.");
380
+ return value;
381
+ }
382
+ function requestOperations(value) {
383
+ if (value === void 0) return [];
384
+ if (!Array.isArray(value) || value.length > 1e5) throw new Error("A bounded operations array is required.");
385
+ return value.map((operation) => {
386
+ if (typeof operation !== "object" || operation === null || typeof operation.fragmentId !== "string" || typeof operation.replacement !== "string") throw new Error("Each operation requires string fragmentId and replacement fields.");
387
+ return operation;
388
+ });
389
+ }
390
+ async function bodyOf(request) {
391
+ const body = await request.json();
392
+ if (typeof body !== "object" || body === null || Array.isArray(body)) throw new Error("A JSON object is required.");
393
+ return body;
394
+ }
395
+ function createAuthorDocumentApi(options = {}) {
396
+ const app = new hono.Hono();
397
+ const cwd = options.cwd ?? process.cwd();
398
+ app.post(apiRoutes_default.documentsOpen.path, async (context) => {
399
+ try {
400
+ const body = await bodyOf(context.req.raw);
401
+ if (body.format === void 0 && options.open !== void 0) return context.json(await options.open(body.path, context.req.raw.signal, body.alias));
402
+ const bytes = await readDocument(cwd, body.path);
403
+ if (body.format === void 0) return context.json({
404
+ path: body.path,
405
+ byteLength: bytes.byteLength
406
+ });
407
+ return context.json(await require_structuredDocuments.parseStructuredDocument(requestFormat(body.format), bytes, body.csvDialect));
408
+ } catch (error) {
409
+ return errorResponse(context, error);
410
+ }
411
+ });
412
+ app.post(apiRoutes_default.documentsPreflight.path, async (context) => {
413
+ try {
414
+ const body = await bodyOf(context.req.raw);
415
+ const bytes = await readDocument(cwd, body.path);
416
+ return context.json(await require_structuredDocuments.preflightStructuredDocument(requestFormat(body.format), bytes, requestOperations(body.operations), body.csvDialect));
417
+ } catch (error) {
418
+ return errorResponse(context, error);
419
+ }
420
+ });
421
+ app.post(apiRoutes_default.documentsSerialize.path, async (context) => {
422
+ try {
423
+ const body = await bodyOf(context.req.raw);
424
+ if (typeof body.preflightDigest !== "string") throw new Error("A preflight report digest is required.");
425
+ const bytes = await readDocument(cwd, body.path);
426
+ const serialized = await require_structuredDocuments.serializeStructuredDocument(requestFormat(body.format), bytes, requestOperations(body.operations), body.preflightDigest, body.csvDialect);
427
+ return context.json({
428
+ bytes: Buffer.from(serialized.bytes).toString("base64"),
429
+ encoding: "base64",
430
+ manifest: serialized.manifest,
431
+ operationLog: serialized.operationLog
432
+ });
433
+ } catch (error) {
434
+ return errorResponse(context, error);
435
+ }
436
+ });
437
+ return app;
438
+ }
439
+ //#endregion
440
+ //#region src/extensions/workspaces/sessions/(backend)/api/author/_lib/authorApi.ts
441
+ const inactiveState = () => ({
442
+ activation: "inactive",
443
+ capabilityCount: 0
444
+ });
445
+ const createBridge = () => require_structuredDocuments.createAuthorBridgeState({
446
+ now: Date.now,
447
+ issueToken: node_crypto.randomUUID,
448
+ scheduleTimeout(callback, delayMs) {
449
+ const timer = setTimeout(callback, delayMs);
450
+ timer.unref?.();
451
+ return () => clearTimeout(timer);
452
+ }
453
+ });
454
+ function createAuthorApi(options = {}) {
455
+ const app = new hono.Hono();
456
+ const registry = options.registry ?? createAuthorCanvasRegistry(options.cwd ?? process.cwd(), createBridge);
457
+ app.get(apiRoutes_default.state.path, (context) => context.json({
458
+ sessionId: options.sessionId ?? null,
459
+ ...(options.readState ?? inactiveState)()
460
+ }));
461
+ app.route("/", createAuthorBridgeApi(registry));
462
+ app.route("/", createAuthorDocumentApi({
463
+ ...options.cwd === void 0 ? {} : { cwd: options.cwd },
464
+ open: (path, signal, alias) => registry.open(path, signal, alias)
465
+ }));
466
+ return app;
467
+ }
468
+ const api = {
469
+ basePath: API_BASE_PATH,
470
+ start(context) {
471
+ if (context.cwd === void 0) throw new Error("Author requires the session working directory.");
472
+ const registry = createAuthorCanvasRegistry(context.cwd, createBridge);
473
+ const app = createAuthorApi({
474
+ ...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
475
+ cwd: context.cwd,
476
+ registry
477
+ });
478
+ return {
479
+ fetch: (request) => app.fetch(request),
480
+ close: () => registry.dispose()
481
+ };
482
+ }
483
+ };
484
+ /** The session API and tools share the same browser ownership and pending requests. */
485
+ function createAuthorSessionApi(cwd, sessionId) {
486
+ const registry = createAuthorCanvasRegistry(cwd, createBridge);
487
+ return {
488
+ closeCanvases: () => registry.closeAll(),
489
+ catalog: {
490
+ open: (path, signal, alias) => registry.open(path, signal, alias),
491
+ async describe(signal, alias) {
492
+ signal?.throwIfAborted();
493
+ return registry.describe(alias);
494
+ },
495
+ execute: (input, signal) => registry.invoke(input, signal)
496
+ },
497
+ api: {
498
+ basePath: API_BASE_PATH,
499
+ start() {
500
+ const app = createAuthorApi({
501
+ cwd,
502
+ sessionId,
503
+ registry
504
+ });
505
+ return {
506
+ fetch: (request) => app.fetch(request),
507
+ close: () => registry.dispose()
508
+ };
509
+ }
510
+ }
511
+ };
512
+ }
513
+ //#endregion
514
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
515
+ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)(({ agent }) => {
516
+ const session = agent ? createAuthorSessionApi(agent.context.cwd, agent.context.sessionId) : void 0;
517
+ const isActive = () => (agent?.context.selection.state?.["minor-mode"] ?? []).includes(require_structuredDocuments.authorMinorMode.descriptor.id);
518
+ const mode = agent ? require_structuredDocuments.authorMinorMode.createOwner({
519
+ isActive,
520
+ detail: () => "document authoring available",
521
+ async setActive(enabled) {
522
+ const modes = (agent.context.selection.state?.["minor-mode"] ?? []).filter((id) => id !== require_structuredDocuments.authorMinorMode.descriptor.id);
523
+ await agent.changeSelection({
524
+ axis: "state",
525
+ key: "minor-mode",
526
+ values: enabled ? [...modes, require_structuredDocuments.authorMinorMode.descriptor.id] : modes
527
+ });
528
+ if (!enabled) session?.closeCanvases();
529
+ }
530
+ }) : void 0;
531
+ return {
532
+ value: {
533
+ api: session?.api ?? api,
534
+ catalog: require_structuredDocuments.createAuthorCatalog(session?.catalog),
535
+ assertAvailable: () => {
536
+ if (!isActive()) throw new Error("Author mode is not active.");
537
+ },
538
+ service: void 0
539
+ },
540
+ services: mode ? [(0, _agimon_ai_po_minor_mode.serverMinorModes)([mode])] : []
541
+ };
542
+ });
543
+ //#endregion
544
+ //#region src/types/webAuthor.ts
545
+ const authorChannelType = "author_webmcp";
546
+ //#endregion
547
+ //#region src/services/webAuthorChannel/index.ts
548
+ /**
549
+ * Which route carries each message the browser sends.
550
+ *
551
+ * The paths come from the package's one route table, so the hub cannot address
552
+ * a bridge route the session no longer serves. `release` is absent because it
553
+ * is answered by the disconnect route rather than one of its own.
554
+ */
555
+ const BRIDGE_ROUTE = {
556
+ register: apiRoutes_default.bridgeRegister.path,
557
+ catalog: apiRoutes_default.bridgeCatalog.path,
558
+ result: apiRoutes_default.bridgeResult.path,
559
+ cancelled: apiRoutes_default.bridgeCancelled.path
560
+ };
561
+ function isRecord(value) {
562
+ return typeof value === "object" && value !== null && !Array.isArray(value);
563
+ }
564
+ function browserMessage(value) {
565
+ if (!isRecord(value) || typeof value.kind !== "string" || typeof value.alias !== "string" || !require_structuredDocuments.AUTHOR_ALIAS_PATTERN.test(value.alias)) return void 0;
566
+ if (![
567
+ "register",
568
+ "release",
569
+ "catalog",
570
+ "result",
571
+ "cancelled"
572
+ ].includes(value.kind)) return void 0;
573
+ return value;
574
+ }
575
+ async function responsePayload(response) {
576
+ const value = await response.json();
577
+ if (response.ok) return value;
578
+ return {
579
+ kind: "rejected",
580
+ reason: isRecord(value) && typeof value.error === "string" ? value.error : `Author bridge HTTP ${response.status}`
581
+ };
582
+ }
583
+ function createAuthorChannel() {
584
+ const bindings = /* @__PURE__ */ new Map();
585
+ let host;
586
+ let closed = false;
587
+ const keyOf = (sessionId, connectionId, alias) => `${sessionId}\0${connectionId}\0${alias}`;
588
+ const send = async (scope, path, value, signal) => await host.requestSessionApi(scope, {
589
+ basePath: API_BASE_PATH,
590
+ path,
591
+ method: "POST",
592
+ body: JSON.stringify(value),
593
+ ...signal === void 0 ? {} : { signal }
594
+ });
595
+ const publish = (binding, payload) => host?.publishToConnection?.(binding.connectionId, binding.scope.sessionId, {
596
+ ...payload,
597
+ alias: binding.alias
598
+ }) ?? false;
599
+ const poll = async (binding) => {
600
+ binding.poll?.abort();
601
+ const controller = new AbortController();
602
+ binding.poll = controller;
603
+ while (!closed && bindings.get(keyOf(binding.scope.sessionId, binding.connectionId, binding.alias)) === binding) try {
604
+ const payload = await responsePayload(await send(binding.scope, apiRoutes_default.bridgeNext.path, {
605
+ alias: binding.alias,
606
+ bindingId: binding.connectionId,
607
+ generation: binding.generation,
608
+ ownerToken: binding.ownerToken
609
+ }, controller.signal));
610
+ if (!publish(binding, payload) || payload.kind === "rejected") break;
611
+ } catch (error) {
612
+ if (!controller.signal.aborted) host?.onNotice(error instanceof Error ? error.message : String(error));
613
+ break;
614
+ }
615
+ };
616
+ return {
617
+ frameType: authorChannelType,
618
+ lifecycle: "hub",
619
+ receiveWithoutSubscription: true,
620
+ start(channelHost) {
621
+ host = channelHost;
622
+ return {
623
+ payloadFor: () => void 0,
624
+ sessionRemoved(sessionId) {
625
+ for (const [key, binding] of bindings) {
626
+ if (binding.scope.sessionId !== sessionId) continue;
627
+ binding.poll?.abort();
628
+ bindings.delete(key);
629
+ send(binding.scope, apiRoutes_default.bridgeDisconnect.path, {
630
+ alias: binding.alias,
631
+ bindingId: binding.connectionId,
632
+ generation: binding.generation
633
+ }).catch((error) => host?.onNotice(error instanceof Error ? error.message : String(error)));
634
+ }
635
+ },
636
+ close() {
637
+ closed = true;
638
+ for (const binding of bindings.values()) {
639
+ binding.poll?.abort();
640
+ send(binding.scope, apiRoutes_default.bridgeDisconnect.path, {
641
+ alias: binding.alias,
642
+ bindingId: binding.connectionId,
643
+ generation: binding.generation
644
+ }).catch((error) => host?.onNotice(error instanceof Error ? error.message : String(error)));
645
+ }
646
+ bindings.clear();
647
+ }
648
+ };
649
+ },
650
+ receive(scope, payload, connection) {
651
+ const message = browserMessage(payload);
652
+ if (message === void 0 || host === void 0 || closed) return;
653
+ (async () => {
654
+ const key = keyOf(scope.sessionId, connection.connectionId, message.alias);
655
+ const previous = bindings.get(key);
656
+ if (message.kind === "release") {
657
+ if (previous === void 0 || previous.generation !== message.generation) return;
658
+ previous.poll?.abort();
659
+ bindings.delete(key);
660
+ await send(scope, apiRoutes_default.bridgeDisconnect.path, {
661
+ alias: message.alias,
662
+ bindingId: connection.connectionId,
663
+ generation: message.generation
664
+ });
665
+ return;
666
+ }
667
+ const route = BRIDGE_ROUTE[message.kind];
668
+ const reply = await responsePayload(await send(scope, route, {
669
+ ...message,
670
+ bindingId: connection.connectionId
671
+ }));
672
+ if (reply.kind === "accepted") {
673
+ const binding = {
674
+ alias: message.alias,
675
+ scope,
676
+ connectionId: connection.connectionId,
677
+ generation: reply.generation,
678
+ ownerToken: reply.ownerToken
679
+ };
680
+ previous?.poll?.abort();
681
+ bindings.set(key, binding);
682
+ publish(binding, reply);
683
+ poll(binding);
684
+ } else if (reply.kind === "rejected") {
685
+ if (previous !== void 0) publish(previous, reply);
686
+ else host?.publishToConnection?.(connection.connectionId, scope.sessionId, {
687
+ ...reply,
688
+ alias: message.alias
689
+ });
690
+ }
691
+ })().catch((error) => host?.onNotice(error instanceof Error ? error.message : String(error)));
692
+ },
693
+ disconnected(connection) {
694
+ for (const [key, binding] of bindings) {
695
+ if (binding.connectionId !== connection.connectionId) continue;
696
+ binding.poll?.abort();
697
+ bindings.delete(key);
698
+ send(binding.scope, apiRoutes_default.bridgeDisconnect.path, {
699
+ alias: binding.alias,
700
+ bindingId: binding.connectionId,
701
+ generation: binding.generation
702
+ }).catch((error) => host?.onNotice(error instanceof Error ? error.message : String(error)));
703
+ }
704
+ }
705
+ };
706
+ }
707
+ //#endregion
708
+ //#region src/extensions/(backend)/channel/author-webmcp.server.ts
709
+ var author_webmcp_server_default = (0, _agimon_ai_po_core_extensionFile.defineChannel)(createAuthorChannel);
710
+ //#endregion
711
+ //#region src/services/authorBridgeMethod/index.ts
712
+ function createAuthorBridgeMethod(scope, host) {
713
+ return (0, _agimon_ai_po_core_serverFacet.defineServerMethod)(require_authorFacade.authorBridgeMethod(scope), ({ sessionId, message }, caller) => {
714
+ if (!caller.connectionId) throw new Error("Author bridge requires an authenticated browser connection.");
715
+ if (!host.context.receiveChannel?.(sessionId, "author_webmcp", message, caller.connectionId)) throw new Error("Author bridge session is outside this mount.");
716
+ return {};
717
+ });
718
+ }
719
+ //#endregion
720
+ //#region src/extensions/(backend)/method/bridge.server.ts
721
+ var bridge_server_default = (0, _agimon_ai_po_core_extensionFile.defineMethod)(({ host }) => host.scope === "session" ? { register: () => ({ dispose: () => void 0 }) } : createAuthorBridgeMethod(host.scope, host));
722
+ //#endregion
723
+ //#region src/extensions/workspaces/sessions/(backend)/api/author/route.server.ts
724
+ var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)((context) => context.root.api);
725
+ //#endregion
726
+ //#region src/extensions/workspaces/sessions/(backend)/command/author.server.ts
727
+ var author_server_default = (0, _agimon_ai_po_core_extensionFile.defineCommand)((context) => require_structuredDocuments.createAuthorCommand(context.root.service));
728
+ //#endregion
729
+ //#region src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts
730
+ var before_agent_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)({
731
+ when: {
732
+ state: { "minor-mode": "author" },
733
+ attribution: {
734
+ kind: "minor",
735
+ mode: "author"
736
+ }
737
+ },
738
+ event: "before_agent_start",
739
+ handle(event) {
740
+ return { systemPrompt: `${typeof event.systemPrompt === "string" ? event.systemPrompt : ""}\n\n${require_structuredDocuments.AUTHOR_GUIDANCE}`.trim() };
741
+ }
742
+ });
743
+ //#endregion
744
+ //#region src/extensions/workspaces/sessions/(backend)/resource/po-use-author.server.ts
745
+ var po_use_author_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)({
746
+ when: {
747
+ state: { "minor-mode": "author" },
748
+ attribution: {
749
+ kind: "minor",
750
+ mode: "author"
751
+ }
752
+ },
753
+ name: "po-use-author",
754
+ kind: "skill",
755
+ read: () => (0, _agimon_ai_po_core_serverFacet.readPackageResource)(require("url").pathToFileURL(__filename).href, "src/prompts/po-use-author/SKILL.md")
756
+ });
757
+ //#endregion
758
+ //#region src/extensions/workspaces/sessions/(backend)/tool/describe-author-tools.server.ts
759
+ var describe_author_tools_server_default = (0, _agimon_ai_po_core_extensionFile.defineTool)((context) => ({
760
+ ...require_structuredDocuments.createAuthorTools(context.root.catalog, context.root.assertAvailable)[1],
761
+ when: require_structuredDocuments.AUTHOR_TOOL_WHEN
762
+ }));
763
+ //#endregion
764
+ //#region src/extensions/workspaces/sessions/(backend)/tool/open-authoring-file.server.ts
765
+ var open_authoring_file_server_default = (0, _agimon_ai_po_core_extensionFile.defineTool)((context) => ({
766
+ ...require_structuredDocuments.createAuthorTools(context.root.catalog, context.root.assertAvailable)[0],
767
+ when: require_structuredDocuments.AUTHOR_TOOL_WHEN
768
+ }));
769
+ //#endregion
770
+ //#region src/extensions/workspaces/sessions/(backend)/tool/use-author-tools.server.ts
771
+ var use_author_tools_server_default = (0, _agimon_ai_po_core_extensionFile.defineTool)((context) => ({
772
+ ...require_structuredDocuments.createAuthorTools(context.root.catalog, context.root.assertAvailable)[2],
773
+ when: require_structuredDocuments.AUTHOR_TOOL_WHEN
774
+ }));
775
+ //#endregion
776
+ //#region generated/server.ts
777
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
778
+ const via = (identity, value) => value === void 0 ? void 0 : {
779
+ ...identity,
780
+ ...value
781
+ };
782
+ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
783
+ name: "@agimon-ai/po-author",
784
+ providerOverrides: (0, _agimon_ai_po_core_serverFacet.composeServerProviderOverrides)(root_server_default),
785
+ global: (context) => ({
786
+ get channels() {
787
+ return [() => via({ frameType: "author_webmcp" }, author_webmcp_server_default())];
788
+ },
789
+ get methods() {
790
+ return [at(bridge_server_default, context)];
791
+ }
792
+ }),
793
+ workspace: (context) => ({
794
+ get channels() {
795
+ return [() => via({ frameType: "author_webmcp" }, author_webmcp_server_default())];
796
+ },
797
+ get methods() {
798
+ return [at(bridge_server_default, context)];
799
+ }
800
+ }),
801
+ session: async (context) => {
802
+ const scopeSession = await root_server_default(context);
803
+ const contextSession = {
804
+ ...context,
805
+ root: scopeSession.value
806
+ };
807
+ return {
808
+ ...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
809
+ get api() {
810
+ return [at(route_server_default, contextSession)];
811
+ },
812
+ get channels() {
813
+ return [() => via({ frameType: "author_webmcp" }, author_webmcp_server_default())];
814
+ },
815
+ get commands() {
816
+ return [via({ name: "author" }, at(author_server_default, contextSession))];
817
+ },
818
+ get hooks() {
819
+ return [via({ event: "before_agent_start" }, at(before_agent_start_server_default, contextSession))];
820
+ },
821
+ get methods() {
822
+ return [at(bridge_server_default, context)];
823
+ },
824
+ get resources() {
825
+ return [at(po_use_author_server_default, contextSession)];
826
+ },
827
+ get tools() {
828
+ return [
829
+ via({ name: "describe_author_tools" }, at(describe_author_tools_server_default, contextSession)),
830
+ via({ name: "open_authoring_file" }, at(open_authoring_file_server_default, contextSession)),
831
+ via({ name: "use_author_tools" }, at(use_author_tools_server_default, contextSession))
832
+ ];
833
+ },
834
+ services: [...scopeSession.services ?? []],
835
+ get activities() {
836
+ return [...scopeSession.activities ?? []];
837
+ }
838
+ };
839
+ }
840
+ });
841
+ //#endregion
842
+ exports.default = facet;
843
+ exports.facet = facet;
844
+
845
+ //# sourceMappingURL=server.cjs.map