@monster-orange/den 0.4.15 → 0.4.17

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 (37) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/README.md +7 -7
  3. package/docs/distributions.md +137 -11
  4. package/package.json +1 -1
  5. package/src/shared/agents/conversations.ts +122 -4
  6. package/src/shared/agents/den-claude.ts +13 -1
  7. package/src/shared/agents/records.ts +19 -0
  8. package/src/shared/agents/runtime.ts +5 -0
  9. package/src/shared/github-app.ts +7 -0
  10. package/src/ui/cli/agent-ops.ts +304 -1
  11. package/src/ui/cli/agents-command.ts +71 -7
  12. package/src/ui/server/agent-routes.ts +92 -2
  13. package/src/ui/server/check-pool.ts +72 -0
  14. package/src/ui/server/github-routes.ts +1 -1
  15. package/src/ui/server/index.ts +127 -15
  16. package/src/ui/server/public/agents-page.js +358 -38
  17. package/src/ui/server/public/agents.html +10 -0
  18. package/src/ui/server/public/environments.html +223 -140
  19. package/src/ui/server/public/error-page.js +70 -15
  20. package/src/ui/server/public/error.html +18 -0
  21. package/src/ui/server/public/graph.css +18 -0
  22. package/src/ui/server/public/graph.js +23 -6
  23. package/src/ui/server/public/i18n/es.js +47 -5
  24. package/src/ui/server/public/i18n/ja.js +47 -5
  25. package/src/ui/server/public/index.html +6 -0
  26. package/src/ui/server/public/local-dev.html +188 -25
  27. package/src/ui/server/public/panel.js +1 -1
  28. package/src/ui/server/public/persona-ui.css +11 -0
  29. package/src/ui/server/public/release.html +19 -1
  30. package/src/ui/server/public/shell.js +187 -12
  31. package/src/ui/server/public/topbar.js +34 -1
  32. package/src/ui/server/public/view-scope.js +61 -0
  33. package/src/ui/server/public/workspace-picker.js +2 -0
  34. package/src/ui/server/route-classes.ts +8 -0
  35. package/src/ui/server/run-history.ts +181 -0
  36. package/src/ui/server/session-routes.ts +97 -17
  37. package/src/ui/server/ui-assets.ts +2 -0
@@ -174,6 +174,8 @@ export interface ListedSession {
174
174
  /** The host has it (running, or ended with its scrollback): a pane can attach. */
175
175
  attachable: boolean;
176
176
  resumable?: boolean;
177
+ /** AG-18: Change persona… applies (its runtime can resume). */
178
+ personaChange?: boolean;
177
179
  /** Why only Start fresh applies (AG-14.2): "<runtime> can't resume a conversation". */
178
180
  noResume?: string;
179
181
  /** When it was stopped, seen to end, or found interrupted (AG-14.1). */
@@ -243,6 +245,7 @@ export function listedSessions(
243
245
  ...(r.startedAt ? { startedAt: r.startedAt } : {}),
244
246
  attachable: Boolean(s),
245
247
  resumable: r.resumable,
248
+ ...(r.personaChange ? { personaChange: true } : {}),
246
249
  ...(r.noResume ? { noResume: r.noResume } : {}),
247
250
  ...(r.stoppedAt ? { stoppedAt: r.stoppedAt } : {}),
248
251
  ...(r.endedAt ? { endedAt: r.endedAt } : {}),
@@ -359,18 +362,101 @@ const TEXT = (code: number) =>
359
362
  : "Service Unavailable";
360
363
 
361
364
  /**
362
- * A plain HTTP request to an io route: it is a WebSocket. 426 for a GET,
363
- * 404 for anything else under /api/sessions/.
365
+ * Whether a session is one the io route serves: this distribution's, in a
366
+ * workspace workspaces.yml names, active or not (0.4.17: an agent you can
367
+ * start and see, you can open). The person's and the agent's checks are
368
+ * the request guard's, before this.
364
369
  */
365
- export function sendSessionHttp(
370
+ function servedSession(
371
+ s: SessionInfo,
372
+ target: string,
373
+ d: Resolved,
374
+ active: ReadonlySet<string>,
375
+ ): boolean {
376
+ const p = parseSessionName(s.name, d.dist());
377
+ return (
378
+ p !== null &&
379
+ (active.has(p.workspace) || d.workspaceExists(p.workspace)) &&
380
+ (s.ref.id === target || s.name === target)
381
+ );
382
+ }
383
+
384
+ /**
385
+ * Why the io route would refuse `target`, or null when it would open it:
386
+ * what a browser can't read from a refused upgrade (it sees only a close),
387
+ * so the page asks this with a plain GET (0.4.17).
388
+ */
389
+ async function ioRefusal(
390
+ target: string,
391
+ d: Resolved,
392
+ ): Promise<{ status: number; error: string; code: string } | null> {
393
+ let c: HostClient | null;
394
+ try {
395
+ c = await d.connectExisting();
396
+ } catch (e) {
397
+ return {
398
+ status: 503,
399
+ error: `the session host: ${(e as Error).message}`,
400
+ code: "session_host",
401
+ };
402
+ }
403
+ if (!c)
404
+ return {
405
+ status: 404,
406
+ error: `no session "${target}" (no session host is running)`,
407
+ code: "not_found",
408
+ };
409
+ try {
410
+ const active = new Set(d.activeWorkspaces());
411
+ const found = (await c.list()).some((s) =>
412
+ servedSession(s, target, d, active),
413
+ );
414
+ return found
415
+ ? null
416
+ : {
417
+ status: 404,
418
+ error: `no session "${target}" here`,
419
+ code: "not_found",
420
+ };
421
+ } catch (e) {
422
+ return {
423
+ status: 503,
424
+ error: `the session host: ${(e as Error).message}`,
425
+ code: "session_host",
426
+ };
427
+ } finally {
428
+ c.close();
429
+ }
430
+ }
431
+
432
+ /**
433
+ * A plain HTTP request to an io route: it is a WebSocket. For a GET, the
434
+ * refusal its upgrade would get (404, 503), else 426; 404 for anything
435
+ * else under /api/sessions/.
436
+ */
437
+ export async function sendSessionHttp(
366
438
  req: http.IncomingMessage,
367
439
  res: http.ServerResponse,
368
440
  pathname: string,
369
- ): void {
370
- if (
371
- IO_PATH.test(pathname) &&
372
- (req.method === "GET" || req.method === "HEAD")
373
- ) {
441
+ deps: SessionRouteDeps = {},
442
+ ): Promise<void> {
443
+ const m = IO_PATH.exec(pathname);
444
+ if (m && (req.method === "GET" || req.method === "HEAD")) {
445
+ let target = m[1];
446
+ try {
447
+ target = decodeURIComponent(target);
448
+ } catch {
449
+ /* as given */
450
+ }
451
+ const why = await ioRefusal(target, resolve(deps));
452
+ if (why) {
453
+ sendJson(
454
+ res,
455
+ { ok: false, error: why.error, code: why.code },
456
+ why.status,
457
+ );
458
+ return;
459
+ }
374
460
  res.setHeader("Upgrade", "websocket");
375
461
  res.setHeader("Connection", "Upgrade");
376
462
  sendJson(
@@ -441,16 +527,10 @@ export async function handleSessionUpgrade(
441
527
  const client = c;
442
528
  let info: SessionInfo | undefined;
443
529
  try {
444
- const dist = d.dist();
445
530
  const active = new Set(d.activeWorkspaces());
446
- info = (await client.list()).find((s) => {
447
- const p = parseSessionName(s.name, dist);
448
- return (
449
- p !== null &&
450
- active.has(p.workspace) &&
451
- (s.ref.id === target || s.name === target)
452
- );
453
- });
531
+ info = (await client.list()).find((s) =>
532
+ servedSession(s, target, d, active),
533
+ );
454
534
  } catch (e) {
455
535
  client.close();
456
536
  return refuse(
@@ -46,6 +46,8 @@ export const UI_MODULES: readonly string[] = [
46
46
  // The UI's catalogs other than English (R19; en is in shell.js).
47
47
  "/i18n/ja.js",
48
48
  "/i18n/es.js",
49
+ // 0.4.17: one org and workspace rule for Environments and Releases.
50
+ "/view-scope.js",
49
51
  ];
50
52
 
51
53
  /** URL path → file inside node_modules. */