@agentproto/apps 0.8.0 → 0.9.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 (60) hide show
  1. package/README.md +29 -1
  2. package/dist/agents-overview/index.d.ts +54 -0
  3. package/dist/agents-overview/index.d.ts.map +1 -0
  4. package/dist/agents-overview/panel.d.ts +14 -0
  5. package/dist/agents-overview/panel.d.ts.map +1 -0
  6. package/dist/agents-overview/panel.mjs +306 -0
  7. package/dist/agents-overview/panel.mjs.map +1 -0
  8. package/dist/agents-overview.mjs +336 -0
  9. package/dist/agents-overview.mjs.map +1 -0
  10. package/dist/bin/sync.mjs +25 -11
  11. package/dist/bin/sync.mjs.map +1 -1
  12. package/dist/bureau-sessions/index.d.ts +52 -0
  13. package/dist/bureau-sessions/index.d.ts.map +1 -0
  14. package/dist/bureau-sessions/panel.d.ts +10 -0
  15. package/dist/bureau-sessions/panel.d.ts.map +1 -0
  16. package/dist/bureau-sessions/panel.mjs +297 -0
  17. package/dist/bureau-sessions/panel.mjs.map +1 -0
  18. package/dist/bureau-sessions.mjs +329 -0
  19. package/dist/bureau-sessions.mjs.map +1 -0
  20. package/dist/content-team/workflows/produce-content.d.ts +14 -2
  21. package/dist/content-team/workflows/produce-content.d.ts.map +1 -1
  22. package/dist/content-team/workflows/produce-cover.d.ts +13 -1
  23. package/dist/content-team/workflows/produce-cover.d.ts.map +1 -1
  24. package/dist/content-team.mjs +25 -11
  25. package/dist/content-team.mjs.map +1 -1
  26. package/dist/index.d.ts +24 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.mjs +2635 -12
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/live-session/index.d.ts +46 -0
  31. package/dist/live-session/index.d.ts.map +1 -0
  32. package/dist/live-session/logic.d.ts +125 -0
  33. package/dist/live-session/logic.d.ts.map +1 -0
  34. package/dist/live-session/panel.d.ts +61 -0
  35. package/dist/live-session/panel.d.ts.map +1 -0
  36. package/dist/live-session/panel.mjs +993 -0
  37. package/dist/live-session/panel.mjs.map +1 -0
  38. package/dist/live-session.mjs +1032 -0
  39. package/dist/live-session.mjs.map +1 -0
  40. package/dist/mcp-app-types.d.ts +33 -0
  41. package/dist/mcp-app-types.d.ts.map +1 -0
  42. package/dist/panel-bridge.d.ts +38 -0
  43. package/dist/panel-bridge.d.ts.map +1 -0
  44. package/dist/session-story/index.d.ts +66 -0
  45. package/dist/session-story/index.d.ts.map +1 -0
  46. package/dist/session-story/panel.d.ts +31 -0
  47. package/dist/session-story/panel.d.ts.map +1 -0
  48. package/dist/session-story/panel.mjs +970 -0
  49. package/dist/session-story/panel.mjs.map +1 -0
  50. package/dist/session-story.mjs +1002 -0
  51. package/dist/session-story.mjs.map +1 -0
  52. package/dist/sessions-panel/index.d.ts +51 -0
  53. package/dist/sessions-panel/index.d.ts.map +1 -0
  54. package/dist/sessions-panel/panel.d.ts +20 -0
  55. package/dist/sessions-panel/panel.d.ts.map +1 -0
  56. package/dist/sessions-panel/panel.mjs +511 -0
  57. package/dist/sessions-panel/panel.mjs.map +1 -0
  58. package/dist/sessions-panel.mjs +545 -0
  59. package/dist/sessions-panel.mjs.map +1 -0
  60. package/package.json +57 -6
@@ -0,0 +1,46 @@
1
+ /**
2
+ * McpApp definition for the live-session widget — a two-pane panel (left:
3
+ * live session tree, right: streaming timeline) that lets a host attach to
4
+ * any running agent session and watch it work in real time.
5
+ *
6
+ * The HTML bundle itself (`LIVE_SESSION_HTML`, its wire protocol, the
7
+ * inlined timeline-reducer copy, compact-mode rendering, …) lives in
8
+ * ./panel.ts, which imports nothing beyond plain TS — see that file for the
9
+ * full protocol/rendering doc. Kept separate so a consumer that only needs
10
+ * the HTML can import `@agentproto/apps/live-session/panel` without pulling
11
+ * in `@agentproto/app-kit` (and therefore `@mastra/core`) the way importing
12
+ * from this file would.
13
+ *
14
+ * This module also exports `liveSessionApp`, a real `defineApp()`
15
+ * `AppHandle` (`agents: []`, UI-only) — the catalog/emit/`app_install` path.
16
+ * Its `ui.html` is a static snapshot (`LIVE_SESSION_HTML` called with the
17
+ * default `httpBaseUrl`) since `AppUiDefinition.ui.html` must be a plain
18
+ * string; it's separate from `makeLiveSessionApp` above, which the daemon
19
+ * mounts directly at boot with the real daemon origin baked in per-call.
20
+ */
21
+ import { z } from "zod";
22
+ import { type AppHandle } from "@agentproto/app-kit";
23
+ import type { AgnoMcpApp } from "../mcp-app-types.js";
24
+ import { LIVE_SESSION_HTML, type LiveSessionOutput } from "./panel.js";
25
+ export { LIVE_SESSION_HTML };
26
+ export type { LiveSessionOutput };
27
+ export declare const liveSessionInputSchema: z.ZodObject<{
28
+ sessionId: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strip>;
30
+ export type LiveSessionInput = z.infer<typeof liveSessionInputSchema>;
31
+ export interface LiveSessionOps {
32
+ /** The daemon's own HTTP origin, e.g. "http://127.0.0.1:18790". Defaults
33
+ * to the daemon's documented default port (`index.ts:776`) so the app
34
+ * works out of the box in the common single-daemon case. */
35
+ httpBaseUrl?: string;
36
+ }
37
+ /**
38
+ * Factory: close over the daemon's own HTTP origin so execute() needs
39
+ * nothing beyond the tool input (no registry access — mirrors
40
+ * sessions-panel/index.ts and runtime's terminal-panel-app.ts; the app_*
41
+ * tools this widget calls over the bridge are what actually touch the
42
+ * registry).
43
+ */
44
+ export declare function makeLiveSessionApp(ops?: LiveSessionOps): AgnoMcpApp<LiveSessionInput, LiveSessionOutput>;
45
+ export declare const liveSessionApp: AppHandle;
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/live-session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEtE,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,YAAY,EAAE,iBAAiB,EAAE,CAAA;AAEjC,eAAO,MAAM,sBAAsB;;iBAQjC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE,MAAM,WAAW,cAAc;IAC7B;;iEAE6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,CAAC,EAAE,cAAc,GACnB,UAAU,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAuBjD;AAID,eAAO,MAAM,cAAc,EAAE,SAY3B,CAAA"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Pure, dependency-free reducer for the live-session widget's timeline.
3
+ *
4
+ * This module is imported by BOTH the test suite AND (as a hand-kept
5
+ * inlined copy, since the widget HTML is a self-contained script with no
6
+ * bundler) `live-session/index.ts`'s browser bundle — see the "INLINED
7
+ * REDUCER COPY" section there. Keep this file plain TS (no zod, no node
8
+ * imports, no external deps) so it stays trivially portable to that copy.
9
+ *
10
+ * Consumes the `events.jsonl` vocabulary (`{seq,ts,kind,...}`, one record
11
+ * per line) documented in `.plans/CONTRACT.md`. Unknown `kind`s are ignored
12
+ * (state passes through unchanged) rather than throwing, since new kinds can
13
+ * appear in the transcript before the widget knows how to render them.
14
+ */
15
+ export interface TimelineEventRecord {
16
+ seq?: number;
17
+ ts?: string | number;
18
+ kind: string;
19
+ sessionId?: string;
20
+ text?: string;
21
+ partial?: boolean;
22
+ toolCallId?: string;
23
+ toolName?: string;
24
+ arguments?: unknown;
25
+ result?: unknown;
26
+ isError?: boolean;
27
+ reason?: string;
28
+ size?: number;
29
+ used?: number;
30
+ cost?: number;
31
+ tokensIn?: number;
32
+ tokensOut?: number;
33
+ [key: string]: unknown;
34
+ }
35
+ interface RowBase {
36
+ /** Stable, deterministic key derived from the record's `seq` (falls back
37
+ * to the row's index when `seq` is absent) — never `Date.now()`/random,
38
+ * so the same event sequence always reduces to identical row ids. */
39
+ id: string;
40
+ seq?: number;
41
+ ts?: string | number;
42
+ sessionId?: string;
43
+ }
44
+ export interface TextRow extends RowBase {
45
+ kind: "text";
46
+ text: string;
47
+ /** Reducer-internal continuation hint: true when the LATEST record folded
48
+ * into this row was flagged `partial` (an explicitly unterminated debounce
49
+ * flush, see transcript-writer.ts). This flag is the ONLY glue signal
50
+ * across an interleave — a non-partial record with no trailing "\n" is the
51
+ * writer's normal end-of-text-block shape, not a mid-line tear. */
52
+ partial?: boolean;
53
+ }
54
+ export interface ToolCallRow extends RowBase {
55
+ kind: "tool-call";
56
+ toolCallId: string;
57
+ toolName: string;
58
+ arguments?: unknown;
59
+ status: "pending" | "ok" | "error";
60
+ result?: unknown;
61
+ }
62
+ export interface TurnEndRow extends RowBase {
63
+ kind: "turn-end";
64
+ reason?: string;
65
+ }
66
+ export interface UsageInfo {
67
+ size?: number;
68
+ used?: number;
69
+ cost?: number;
70
+ tokensIn?: number;
71
+ tokensOut?: number;
72
+ /** seq/ts of the record that produced this snapshot — last-write-wins. */
73
+ seq?: number;
74
+ ts?: string | number;
75
+ }
76
+ export type TimelineRow = TextRow | ToolCallRow | TurnEndRow;
77
+ export interface TimelineState {
78
+ rows: TimelineRow[];
79
+ /** Latest known usage snapshot, or null before the first usage_update
80
+ * record. Not a row — state, updated in place. */
81
+ usage: UsageInfo | null;
82
+ }
83
+ export declare function initialTimelineState(): TimelineState;
84
+ /**
85
+ * Reduce one `events.jsonl` record into the next timeline state. Pure: never
86
+ * mutates `state` or the record, always returns a fresh state object (or the
87
+ * same `state` reference for an ignored/unknown kind, so callers can cheaply
88
+ * check `next === prev` to skip a re-render).
89
+ */
90
+ export declare function reduceEvent(state: TimelineState, record: TimelineEventRecord): TimelineState;
91
+ export declare const SCROLL_STICK_THRESHOLD_PX = 24;
92
+ /** True when the viewport is close enough to the bottom that new content
93
+ * should auto-follow. Pure arithmetic — no DOM access — so it's testable
94
+ * outside a browser and the DOM layer can call it with live element
95
+ * metrics (`el.scrollHeight`, `el.scrollTop`, `el.clientHeight`). */
96
+ export declare function isNearBottom(scrollHeight: number, scrollTop: number, clientHeight: number, threshold?: number): boolean;
97
+ export declare const TOOL_CALL_GROUP_THRESHOLD = 2;
98
+ export type RowGroupEntry = {
99
+ kind: "row";
100
+ row: TimelineRow;
101
+ } | {
102
+ kind: "tool-group";
103
+ rows: ToolCallRow[];
104
+ };
105
+ /** Walk `rows` once (order preserved) and collapse runs of `threshold`+
106
+ * adjacent tool-call rows into a single `tool-group` entry. A run breaks
107
+ * on any non-tool-call row in between (text, turn-end). Non-tool-call rows,
108
+ * and tool-call runs shorter than `threshold`, pass through as individual
109
+ * `{kind:"row"}` entries — same order as `rows`. */
110
+ export declare function groupAdjacentToolCalls(rows: readonly TimelineRow[], threshold?: number): RowGroupEntry[];
111
+ /**
112
+ * Pull a session id out of an ext-apps `ui/notifications/tool-result`
113
+ * payload — the host's push of the tool call that mounted the widget.
114
+ * Handles both wrapping shapes seen in hosts (`params` IS the
115
+ * CallToolResult, or nests it under `params.result`) and both body shapes
116
+ * this widget can be mounted from: an `agent_start` result (a session
117
+ * descriptor whose id lives in `id`) and a `live_session` result
118
+ * (`{sessionId, httpBaseUrl}`). `sessionId` wins over `id` when both are
119
+ * present. Returns `null` for error results, non-JSON text, or any shape
120
+ * mismatch — never throws. A hand-kept inlined copy lives in
121
+ * `live-session/index.ts`'s browser bundle (same convention as the reducer).
122
+ */
123
+ export declare function extractToolResultSessionId(params: unknown): string | null;
124
+ export {};
125
+ //# sourceMappingURL=logic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logic.d.ts","sourceRoot":"","sources":["../../src/live-session/logic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * HTML bundle for the agentproto live-session widget — `LIVE_SESSION_HTML`,
3
+ * a two-pane panel (left: live session tree, right: streaming timeline)
4
+ * that lets a host attach to any running agent session and watch it work in
5
+ * real time. Mounted via the `live_session` tool defined in ./index.ts.
6
+ *
7
+ * Kept in its own module, importing nothing beyond plain TS, so a consumer
8
+ * that only needs the HTML (e.g. packages/vscode's story-panel-style
9
+ * webviews, or a future live-session webview) can import it via the
10
+ * `@agentproto/apps/live-session/panel` subpath without pulling in
11
+ * `@agentproto/app-kit` (and therefore `@mastra/core`) the way importing
12
+ * from `./index.js` would.
13
+ *
14
+ * Forked structurally from terminal-panel-app.ts: same "static ui://
15
+ * resource renders with empty initData, widget self-bootstraps via
16
+ * tools/call" pattern. Unlike the terminal (one WebSocket, one PTY), this
17
+ * widget has two independent data needs — a polled tree snapshot and a
18
+ * per-session event timeline — so it layers on top of the bridge rather
19
+ * than replacing it with a single socket.
20
+ *
21
+ * Data paths (see `.plans/CONTRACT.md` §WP-B):
22
+ * LEFT (tree): poll `app_session_tree` via `callTool` every ~2s.
23
+ * RIGHT (timeline): PRIMARY = `EventSource` on `/sessions/:id/events/stream`
24
+ * (opportunistic — falls back if it doesn't open/emit
25
+ * within ~2.5s); FALLBACK = poll `app_session_events`
26
+ * via `callTool` every ~1.5s, advancing `since`.
27
+ * Both app_* tools are registered by WP-A (`app-pull-tools.ts`) and are
28
+ * `visibility: ["app"]`-only, i.e. reachable solely over this bridge.
29
+ *
30
+ * The timeline reducer lives in `live-session/logic.ts` as a pure,
31
+ * dependency-free module so it's unit-testable outside the browser. This
32
+ * file inlines a hand-kept COPY of that same logic into the HTML bundle
33
+ * (see "INLINED REDUCER COPY" below) since the widget ships as one
34
+ * self-contained script with no bundler step. Keep the two in sync. Its MCP
35
+ * Apps handshake comes from the shared spec-correct panel bridge.
36
+ *
37
+ * Rendering (WP1): the timeline body is patched INCREMENTALLY — a
38
+ * text-delta that merges into the last row patches only that row's text
39
+ * node (found via `data-row-id`); every other new row is appended with one
40
+ * `insertAdjacentHTML`. A full `innerHTML` reset happens only on session
41
+ * start/`setFocus()` and on display-mode switches. Auto-scroll decision is
42
+ * `isNearBottom` (inlined copy, SPEC §2) captured BEFORE any DOM mutation:
43
+ * stick to bottom when the user was at the bottom, otherwise preserve the
44
+ * read position exactly and surface a "new messages" pill.
45
+ *
46
+ * Compact mode (WP3/WP4): when `hostContext.displayMode === 'inline'` (or
47
+ * the viewport is under ~640px) the tree pane collapses into a header
48
+ * `<select>` (WP4) and the timeline renders through `groupAdjacentToolCalls`
49
+ * (inlined copy, SPEC §3) with a colour rail and collapsible tool-call
50
+ * groups. `setFocus()` remains the only session-switch entry point.
51
+ */
52
+ export type LiveSessionOutput = {
53
+ /** Echoes the input id, or undefined to let the widget self-discover a
54
+ * focus session client-side from `app_session_tree`. */
55
+ sessionId?: string;
56
+ /** The daemon's own HTTP origin — connection recipe for both the SSE
57
+ * stream and the bridge's `tools/call` fallback. */
58
+ httpBaseUrl: string;
59
+ };
60
+ export declare function LIVE_SESSION_HTML(initData: LiveSessionOutput): string;
61
+ //# sourceMappingURL=panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/live-session/panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAIH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;6DACyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;yDACqD;IACrD,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAID,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAs0BrE"}