agent-approvals 0.7.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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +164 -0
  3. package/dist/ApprovalGate-CRRh36j-.js +270 -0
  4. package/dist/ApprovalGate-CRRh36j-.js.map +1 -0
  5. package/dist/agent-approvals.css +2 -0
  6. package/dist/ai-sdk/ApprovalGate.d.ts +39 -0
  7. package/dist/ai-sdk/ApprovalGate.d.ts.map +1 -0
  8. package/dist/ai-sdk/index.d.ts +5 -0
  9. package/dist/ai-sdk/index.d.ts.map +1 -0
  10. package/dist/ai-sdk/toApprovalRequests.d.ts +10 -0
  11. package/dist/ai-sdk/toApprovalRequests.d.ts.map +1 -0
  12. package/dist/ai-sdk/types.d.ts +61 -0
  13. package/dist/ai-sdk/types.d.ts.map +1 -0
  14. package/dist/ai-sdk.js +139 -0
  15. package/dist/ai-sdk.js.map +1 -0
  16. package/dist/components/ActivityTrail/ActivityTrail.d.ts +14 -0
  17. package/dist/components/ActivityTrail/ActivityTrail.d.ts.map +1 -0
  18. package/dist/components/ActivityTrail/types.d.ts +39 -0
  19. package/dist/components/ActivityTrail/types.d.ts.map +1 -0
  20. package/dist/components/ApprovalGate/ApprovalGate.d.ts +26 -0
  21. package/dist/components/ApprovalGate/ApprovalGate.d.ts.map +1 -0
  22. package/dist/components/ApprovalGate/HoldToConfirm.d.ts +43 -0
  23. package/dist/components/ApprovalGate/HoldToConfirm.d.ts.map +1 -0
  24. package/dist/components/ApprovalGate/types.d.ts +66 -0
  25. package/dist/components/ApprovalGate/types.d.ts.map +1 -0
  26. package/dist/components/SessionStrip/SessionStrip.d.ts +18 -0
  27. package/dist/components/SessionStrip/SessionStrip.d.ts.map +1 -0
  28. package/dist/components/SessionStrip/types.d.ts +31 -0
  29. package/dist/components/SessionStrip/types.d.ts.map +1 -0
  30. package/dist/components/StatusIndicator/StatusIndicator.d.ts +17 -0
  31. package/dist/components/StatusIndicator/StatusIndicator.d.ts.map +1 -0
  32. package/dist/components/StatusIndicator/types.d.ts +43 -0
  33. package/dist/components/StatusIndicator/types.d.ts.map +1 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +245 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/theme-macos.css +209 -0
  39. package/package.json +97 -0
@@ -0,0 +1,61 @@
1
+ import type { ApprovalGateProps, Reversibility } from '../components/ApprovalGate/types';
2
+ /**
3
+ * The narrowest possible description of an AI SDK message.
4
+ *
5
+ * This adapter deliberately does **not** import the `ai` package, not even for
6
+ * types. It reads the shape of a tool part instead of borrowing a definition of
7
+ * it. Three reasons:
8
+ *
9
+ * - Nothing to install. There is no peer dependency to satisfy and nothing
10
+ * breaks for people who never touch the AI SDK.
11
+ * - No version pin. The SDK renumbers often, and a package that names a version
12
+ * is a package that goes stale on somebody else's schedule.
13
+ * - TypeScript is structural, so a real `UIMessage[]` is assignable to this
14
+ * without a cast.
15
+ *
16
+ * The trade is that we validate at runtime rather than trusting a type, which
17
+ * is the correct trade for data arriving from another library.
18
+ */
19
+ export interface AgentMessageLike {
20
+ parts?: readonly unknown[];
21
+ }
22
+ /**
23
+ * How to answer "can this be undone" for one tool.
24
+ *
25
+ * A plain `true` or `false` covers most tools. The function form exists for the
26
+ * ones where the arguments decide, `runCommand` being the obvious case: `ls`
27
+ * and `rm -rf` are the same tool with the same name.
28
+ */
29
+ export type ReversibleRule = Reversibility | ((input: any) => Reversibility);
30
+ /**
31
+ * Where the gate learns which actions can be undone.
32
+ *
33
+ * Either a lookup keyed by tool name, or one function asked about everything.
34
+ * A tool that is missing from the map, or a function returning `undefined`,
35
+ * falls through to whatever the tool declared about itself, and then to
36
+ * `'unknown'`. Silence is never read as consent.
37
+ */
38
+ export type ReversibleOption = Record<string, ReversibleRule> | ((toolName: string, input: unknown) => Reversibility | undefined);
39
+ /**
40
+ * How to turn a tool call into the sentence a person actually reads.
41
+ *
42
+ * Without this the gate falls back to the tool's own name, tidied up. That is
43
+ * an honest label rather than a written sentence, which is the right default:
44
+ * a fabricated sentence that sounds considered is worse than a plain one that
45
+ * admits what it is.
46
+ */
47
+ export type DescribeOption = Record<string, (input: any) => string> | ((toolName: string, input: unknown) => string | undefined);
48
+ export interface ToApprovalRequestsOptions {
49
+ reversible?: ReversibleOption;
50
+ describe?: DescribeOption;
51
+ }
52
+ export interface AgentApprovalGateProps extends Pick<ApprovalGateProps, 'title' | 'className' | 'onDismiss'>, ToApprovalRequestsOptions {
53
+ /** `messages` straight from `useChat`. */
54
+ messages: readonly AgentMessageLike[];
55
+ /** `addToolApprovalResponse` straight from `useChat`. */
56
+ addToolApprovalResponse: (response: {
57
+ id: string;
58
+ approved: boolean;
59
+ }) => void;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ai-sdk/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAExF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GACtB,aAAa,GAEb,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,aAAa,CAAC,CAAA;AAEnC;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAC9B,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,aAAa,GAAG,SAAS,CAAC,CAAA;AAErE;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAEtB,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAC,GACtC,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC,CAAA;AAE9D,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B;AAED,MAAM,WAAW,sBACf,SAAQ,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,WAAW,GAAG,WAAW,CAAC,EAClE,yBAAyB;IAC3B,0CAA0C;IAC1C,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACrC,yDAAyD;IACzD,uBAAuB,EAAE,CAAC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;CAC/E"}
package/dist/ai-sdk.js ADDED
@@ -0,0 +1,139 @@
1
+ import { t as e } from "./ApprovalGate-CRRh36j-.js";
2
+ import { jsx as t } from "react/jsx-runtime";
3
+ import { useEffect as n, useMemo as r, useRef as i } from "react";
4
+ //#region src/ai-sdk/toApprovalRequests.ts
5
+ var a = "approval-requested";
6
+ function o(e) {
7
+ return typeof e == "object" && !!e;
8
+ }
9
+ function s(e, t) {
10
+ return Object.prototype.hasOwnProperty.call(e, t);
11
+ }
12
+ function c(e) {
13
+ let t = e.type;
14
+ if (typeof t == "string") return t === "dynamic-tool" ? typeof e.toolName == "string" ? e.toolName : void 0 : t.startsWith("tool-") ? t.slice(5) : void 0;
15
+ }
16
+ function l(e) {
17
+ if (!o(e) || e.state !== a) return;
18
+ let t = e.approval;
19
+ if (!o(t) || t.isAutomatic === !0) return;
20
+ let n = t.id;
21
+ if (typeof n != "string" || n === "") return;
22
+ let r = c(e);
23
+ if (r !== void 0 && r !== "") return {
24
+ id: n,
25
+ toolName: r,
26
+ input: e.input,
27
+ metadata: e.toolMetadata
28
+ };
29
+ }
30
+ function u(e) {
31
+ let t = e.replace(/[_-]+/g, " ").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
32
+ if (t === "") return e;
33
+ let n = t.toLowerCase();
34
+ return n.charAt(0).toUpperCase() + n.slice(1);
35
+ }
36
+ function d(e) {
37
+ if (e != null) {
38
+ if (typeof e == "string") return e === "" ? void 0 : e;
39
+ try {
40
+ let t = JSON.stringify(e);
41
+ return t === void 0 || t === "{}" || t === "[]" ? void 0 : t;
42
+ } catch {
43
+ return;
44
+ }
45
+ }
46
+ }
47
+ function f(e) {
48
+ try {
49
+ return e();
50
+ } catch {
51
+ return;
52
+ }
53
+ }
54
+ function p(e) {
55
+ if (e === !0 || e === !1) return e;
56
+ if (e === "unknown") return "unknown";
57
+ }
58
+ function m(e, t, n, r) {
59
+ if (typeof r == "function") {
60
+ let n = p(f(() => r(e, t)));
61
+ if (n !== void 0) return n;
62
+ } else if (o(r) && s(r, e)) {
63
+ let n = r[e], i = p(typeof n == "function" ? f(() => n(t)) : n);
64
+ if (i !== void 0) return i;
65
+ }
66
+ if (o(n)) {
67
+ let e = p(n.reversible);
68
+ if (e !== void 0) return e;
69
+ }
70
+ return "unknown";
71
+ }
72
+ function h(e, t, n) {
73
+ let r;
74
+ if (typeof n == "function") r = f(() => n(e, t));
75
+ else if (o(n) && s(n, e)) {
76
+ let i = n[e];
77
+ typeof i == "function" && (r = f(() => i(t)));
78
+ }
79
+ return typeof r == "string" && r !== "" ? r : u(e);
80
+ }
81
+ function g(e, t = {}) {
82
+ let n = [], r = /* @__PURE__ */ new Set();
83
+ for (let i of e ?? []) {
84
+ let e = o(i) ? i.parts : void 0;
85
+ if (Array.isArray(e)) for (let i of e) {
86
+ let e = l(i);
87
+ !e || r.has(e.id) || (r.add(e.id), n.push({
88
+ id: e.id,
89
+ consequence: h(e.toolName, e.input, t.describe),
90
+ detail: d(e.input),
91
+ reversible: m(e.toolName, e.input, e.metadata, t.reversible)
92
+ }));
93
+ }
94
+ }
95
+ return n;
96
+ }
97
+ //#endregion
98
+ //#region src/ai-sdk/ApprovalGate.tsx
99
+ function _() {
100
+ try {
101
+ return process.env.NODE_ENV !== "production";
102
+ } catch {
103
+ return !0;
104
+ }
105
+ }
106
+ var v = "agent-approvals: every approval is being shown as \"might not be possible to undo\", because nothing has said which of your tools are reversible.\n\nThat is the safe answer, but it means all of them, including harmless ones, need the full hold. Ceremony on everything is how people learn to stop reading it.\n\nDeclare it in either place:\n\n on the tool tool({ ..., metadata: { reversible: true } })\n or at the gate <ApprovalGate reversible={{ searchWeb: true }} ... />\n\nThis message is not in production builds.";
107
+ function y(e, t) {
108
+ let r = i(!1);
109
+ n(() => {
110
+ r.current || t || !_() || e.length !== 0 && e.every((e) => e.reversible === "unknown") && (r.current = !0, console.warn(v));
111
+ }, [e, t]);
112
+ }
113
+ function b({ messages: n, addToolApprovalResponse: i, reversible: a, describe: o, ...s }) {
114
+ let c = r(() => g(n, {
115
+ reversible: a,
116
+ describe: o
117
+ }), [
118
+ n,
119
+ a,
120
+ o
121
+ ]);
122
+ y(c, a !== void 0);
123
+ let l = (e, t) => {
124
+ for (let n of e) i({
125
+ id: n,
126
+ approved: t
127
+ });
128
+ };
129
+ return /* @__PURE__ */ t(e, {
130
+ requests: c,
131
+ onApprove: (e) => l(e, !0),
132
+ onDeny: (e) => l(e, !1),
133
+ ...s
134
+ });
135
+ }
136
+ //#endregion
137
+ export { b as ApprovalGate, g as toApprovalRequests };
138
+
139
+ //# sourceMappingURL=ai-sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk.js","names":[],"sources":["../src/ai-sdk/toApprovalRequests.ts","../src/ai-sdk/ApprovalGate.tsx"],"sourcesContent":["import type { ApprovalRequest, Reversibility } from '../components/ApprovalGate/types'\nimport type {\n AgentMessageLike,\n DescribeOption,\n ReversibleOption,\n ToApprovalRequestsOptions,\n} from './types'\n\n/** The one state that means a person still has to answer. */\nconst AWAITING = 'approval-requested'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n/** Own properties only, so a tool called `toString` cannot inherit a rule. */\nfunction own(source: object, key: string) {\n return Object.prototype.hasOwnProperty.call(source, key)\n}\n\n/**\n * Tools known at build time arrive as `tool-deleteFile`. Tools discovered at\n * runtime, which in practice means anything from an MCP server, arrive as\n * `dynamic-tool` with the name in its own field.\n */\nfunction toolNameOf(part: Record<string, unknown>) {\n const type = part.type\n if (typeof type !== 'string') return undefined\n if (type === 'dynamic-tool') {\n return typeof part.toolName === 'string' ? part.toolName : undefined\n }\n return type.startsWith('tool-') ? type.slice(5) : undefined\n}\n\n/**\n * A tool part that is genuinely still waiting on a human.\n *\n * Three things get filtered out here, and leaving any of them in would be a\n * bug you would only notice in front of a user:\n *\n * - Anything not in the `approval-requested` state. There are separate states\n * for answered and denied calls, and an adapter that matched on \"has an\n * approval object\" would leave settled requests in the gate forever.\n * - Anything the SDK decided on its own. Those still arrive as parts, and\n * showing them would ask a person to decide something already decided.\n * - Anything without a usable id, because the id is what the answer is sent\n * back with, and a request nobody can answer must never be displayed.\n */\nfunction awaitingAnswer(part: unknown) {\n if (!isRecord(part) || part.state !== AWAITING) return undefined\n\n const approval = part.approval\n if (!isRecord(approval) || approval.isAutomatic === true) return undefined\n\n const id = approval.id\n if (typeof id !== 'string' || id === '') return undefined\n\n const toolName = toolNameOf(part)\n if (toolName === undefined || toolName === '') return undefined\n\n return { id, toolName, input: part.input, metadata: part.toolMetadata }\n}\n\n/** `deleteFile` becomes \"Delete file\". `read_HTML_file` becomes \"Read html file\". */\nfunction humanise(name: string) {\n const spaced = name\n .replace(/[_-]+/g, ' ')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .trim()\n if (spaced === '') return name\n const lower = spaced.toLowerCase()\n return lower.charAt(0).toUpperCase() + lower.slice(1)\n}\n\n/**\n * The arguments, shown small underneath the sentence.\n *\n * Never shortened. A safety surface that truncates is a safety surface that\n * hides the interesting half of `rm -rf`, and the stylesheet already wraps\n * long values rather than letting them push the layout around.\n */\nfunction detailOf(input: unknown) {\n if (input === undefined || input === null) return undefined\n if (typeof input === 'string') return input === '' ? undefined : input\n try {\n const text = JSON.stringify(input)\n return text === undefined || text === '{}' || text === '[]' ? undefined : text\n } catch {\n // Circular or otherwise unserialisable. Better no detail than a crash.\n return undefined\n }\n}\n\n/**\n * Runs a rule the developer wrote, and survives it.\n *\n * These functions are host code running inside our render. A rule that reads\n * `input.path` and meets a tool call without one throws, and an exception here\n * does not fail one row: it unmounts the gate and takes every pending approval\n * off the screen with it, including the ones that cannot be undone. Losing one\n * answer to `'unknown'` is the small failure. Losing the whole surface is the\n * one that matters, and it is silent.\n *\n * `detailOf` already takes this care around `JSON.stringify`. Same reasoning.\n */\nfunction attempt<T>(run: () => T): T | undefined {\n try {\n return run()\n } catch {\n return undefined\n }\n}\n\n/** Only a real boolean counts. A string \"true\" is not a declaration. */\nfunction asReversibility(value: unknown): Reversibility | undefined {\n if (value === true || value === false) return value\n if (value === 'unknown') return 'unknown'\n return undefined\n}\n\n/**\n * Three places an answer can come from, asked in order.\n *\n * 1. What the developer said at the gate. They own the app, so they can\n * override a third party tool that is wrong about itself.\n * 2. What the tool declared about itself, via `metadata` on the server, which\n * the SDK copies onto the part as `toolMetadata`. This is the one that\n * scales, because the person who wrote the tool is the person who knows.\n * 3. Nobody said, so `'unknown'`.\n *\n * There is no fourth step where something becomes reversible by accident.\n */\nfunction resolveReversible(\n toolName: string,\n input: unknown,\n metadata: unknown,\n option: ReversibleOption | undefined,\n): Reversibility {\n if (typeof option === 'function') {\n const answer = asReversibility(attempt(() => option(toolName, input)))\n if (answer !== undefined) return answer\n } else if (isRecord(option) && own(option, toolName)) {\n const rule = option[toolName]\n const answer = asReversibility(\n typeof rule === 'function' ? attempt(() => rule(input)) : rule,\n )\n if (answer !== undefined) return answer\n }\n\n if (isRecord(metadata)) {\n const declared = asReversibility(metadata.reversible)\n if (declared !== undefined) return declared\n }\n\n return 'unknown'\n}\n\nfunction resolveConsequence(\n toolName: string,\n input: unknown,\n describe: DescribeOption | undefined,\n) {\n let written: unknown\n if (typeof describe === 'function') {\n written = attempt(() => describe(toolName, input))\n } else if (isRecord(describe) && own(describe, toolName)) {\n const rule = describe[toolName]\n if (typeof rule === 'function') written = attempt(() => rule(input))\n }\n return typeof written === 'string' && written !== '' ? written : humanise(toolName)\n}\n\n/**\n * Turns AI SDK messages into the requests `ApprovalGate` understands.\n *\n * Pure, so it can be tested without a model, a server or a browser. Pass\n * `messages` from `useChat` straight in.\n */\nexport function toApprovalRequests(\n messages: readonly AgentMessageLike[] | undefined,\n options: ToApprovalRequestsOptions = {},\n): ApprovalRequest[] {\n const requests: ApprovalRequest[] = []\n const seen = new Set<string>()\n\n for (const message of messages ?? []) {\n const parts = isRecord(message) ? message.parts : undefined\n if (!Array.isArray(parts)) continue\n\n for (const part of parts) {\n const pending = awaitingAnswer(part)\n // One approval is one decision, however many times it appears.\n if (!pending || seen.has(pending.id)) continue\n seen.add(pending.id)\n\n requests.push({\n id: pending.id,\n consequence: resolveConsequence(pending.toolName, pending.input, options.describe),\n detail: detailOf(pending.input),\n reversible: resolveReversible(\n pending.toolName,\n pending.input,\n pending.metadata,\n options.reversible,\n ),\n })\n }\n }\n\n return requests\n}\n","import { useEffect, useMemo, useRef } from 'react'\nimport { ApprovalGate as Gate } from '../components/ApprovalGate/ApprovalGate'\nimport type { ApprovalRequest } from '../components/ApprovalGate/types'\nimport { toApprovalRequests } from './toApprovalRequests'\nimport type { AgentApprovalGateProps } from './types'\n\n// Bundlers replace this expression at build time. Declared locally because this\n// package does not depend on Node types, and read through a guard because some\n// runtimes have no `process` at all.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nfunction inDevelopment() {\n try {\n return process.env.NODE_ENV !== 'production'\n } catch {\n return true\n }\n}\n\nconst ADVICE = `agent-approvals: every approval is being shown as \"might not be possible to undo\", because nothing has said which of your tools are reversible.\n\nThat is the safe answer, but it means all of them, including harmless ones, need the full hold. Ceremony on everything is how people learn to stop reading it.\n\nDeclare it in either place:\n\n on the tool tool({ ..., metadata: { reversible: true } })\n or at the gate <ApprovalGate reversible={{ searchWeb: true }} ... />\n\nThis message is not in production builds.`\n\n/**\n * Warns once, and only when nobody has said anything at all.\n *\n * Deliberately not thrown and not rendered. A library that breaks a build over\n * a default has overstepped, and a library that puts its own scolding on screen\n * has taken over an interface that belongs to somebody else.\n */\nfunction useUndeclaredWarning(requests: ApprovalRequest[], configured: boolean) {\n const warned = useRef(false)\n\n useEffect(() => {\n if (warned.current || configured || !inDevelopment()) return\n if (requests.length === 0) return\n if (!requests.every((request) => request.reversible === 'unknown')) return\n\n warned.current = true\n console.warn(ADVICE)\n }, [requests, configured])\n}\n\n/**\n * `ApprovalGate`, wired to the AI SDK.\n *\n * The SDK gives developers an `approval-requested` state and no interface at\n * all, and its own cookbook tells them to build the buttons themselves. This is\n * the interface. Hand it `messages` and `addToolApprovalResponse` from\n * `useChat` and it works.\n *\n * ```tsx\n * import { ApprovalGate } from 'agent-approvals/ai-sdk'\n * import 'agent-approvals/styles.css'\n *\n * // Outside the component, not inline in the JSX. Which of your tools can be\n * // undone does not change between renders, and an object literal written in\n * // the middle of the markup is a new object every time, which defeats the\n * // memo below and rescans every message on every streamed token.\n * const REVERSIBLE = { searchWeb: true, readFile: true }\n *\n * const { messages, addToolApprovalResponse } = useChat()\n *\n * <ApprovalGate\n * messages={messages}\n * addToolApprovalResponse={addToolApprovalResponse}\n * reversible={REVERSIBLE}\n * />\n * ```\n *\n * Renders nothing when there is nothing to answer, so it can sit permanently in\n * a layout without being conditionally mounted.\n *\n * This file writes no markup of its own. It reads the shape of a message, works\n * out what is waiting, and hands that to the component. Everything to do with\n * keyboard, focus and screen readers lives in `components/ApprovalGate`, which\n * is the thing that actually owns the DOM. Counting the attributes in this file\n * measures nothing.\n */\nexport function ApprovalGate({\n messages,\n addToolApprovalResponse,\n reversible,\n describe,\n ...rest\n}: AgentApprovalGateProps) {\n // Keyed on identity, deliberately: a rule that changes has to take effect.\n // The cost of that is on the caller, so the documented example hoists its\n // options out of the render rather than writing them inline.\n const requests = useMemo(\n () => toApprovalRequests(messages, { reversible, describe }),\n [messages, reversible, describe],\n )\n\n useUndeclaredWarning(requests, reversible !== undefined)\n\n // Reversible requests can arrive together, so an answer can cover several\n // ids. The SDK takes them one at a time.\n const answer = (ids: string[], approved: boolean) => {\n for (const id of ids) addToolApprovalResponse({ id, approved })\n }\n\n return (\n <Gate\n requests={requests}\n onApprove={(ids) => answer(ids, true)}\n onDeny={(ids) => answer(ids, false)}\n {...rest}\n />\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ActivityTrailProps } from './types';
2
+ import './ActivityTrail.css';
3
+ /**
4
+ * What the agent did, compressed and scannable.
5
+ *
6
+ * A log dump is a list of everything that happened, in the order it happened,
7
+ * at the same volume. Nobody reads one. This is the same information with the
8
+ * repetition folded up and the two things worth finding, the questions and the
9
+ * failures, left standing at full size.
10
+ *
11
+ * It is a record. There is nothing to click except opening a folded group.
12
+ */
13
+ export declare function ActivityTrail({ activities, maxVisible, summarise, label, className, }: ActivityTrailProps): import("react").JSX.Element | null;
14
+ //# sourceMappingURL=ActivityTrail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActivityTrail.d.ts","sourceRoot":"","sources":["../../../src/components/ActivityTrail/ActivityTrail.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA0B,kBAAkB,EAAE,MAAM,SAAS,CAAA;AACzE,OAAO,qBAAqB,CAAA;AA2E5B;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAc,EACd,SAA4B,EAC5B,KAAkB,EAClB,SAAS,GACV,EAAE,kBAAkB,sCAsFpB"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What kind of thing happened.
3
+ *
4
+ * `ask` and `error` are the exceptions, and exceptions are never folded away.
5
+ * Everything else is ordinary and may be collapsed when it repeats.
6
+ */
7
+ export type ActivityKind = 'read' | 'edit' | 'run' | 'ask' | 'error' | 'note';
8
+ export interface Activity {
9
+ /** Your own identifier. */
10
+ id: string;
11
+ kind: ActivityKind;
12
+ /** What happened, in plain words. "Read src/auth.ts" */
13
+ summary: string;
14
+ /** The command, the path, the message. Shown smaller. Optional. */
15
+ detail?: string;
16
+ /** When, already formatted. "14:02", "2m ago". No date logic lives here. */
17
+ at?: string;
18
+ }
19
+ export interface ActivityTrailProps {
20
+ /** Oldest first. The trail reads downwards, the way it happened. */
21
+ activities: Activity[];
22
+ /**
23
+ * How many rows to show before the older ones fold into a single line at the
24
+ * top. Defaults to 8. Groups count as one row.
25
+ */
26
+ maxVisible?: number;
27
+ /**
28
+ * The wording for a collapsed group, given the kind and how many were folded
29
+ * into it. Replace it for other languages or other vocabularies.
30
+ *
31
+ * The default says things like "Read 12 files" and "Ran 3 commands".
32
+ */
33
+ summarise?: (kind: ActivityKind, count: number) => string;
34
+ /** Accessible name for the list. Defaults to "Activity". */
35
+ label?: string;
36
+ /** Your own class, for spacing and positioning from the outside. */
37
+ className?: string;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/ActivityTrail/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAA;AAE7E,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,YAAY,CAAA;IAClB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,UAAU,EAAE,QAAQ,EAAE,CAAA;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACzD,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
@@ -0,0 +1,26 @@
1
+ import type { ApprovalGateProps } from './types';
2
+ import '../sr-only.css';
3
+ import './ApprovalGate.css';
4
+ /**
5
+ * Asks for a human decision without becoming a toll booth.
6
+ *
7
+ * Reversible requests batch: one button clears all of them at once, because
8
+ * being asked four separate times about four safe things is what trains people
9
+ * to stop reading.
10
+ *
11
+ * Everything else never batches. It sits below a rule, collapsed, with no
12
+ * approve control at all until it has been opened, and then it is held rather
13
+ * than clicked. That is the point: the motion that approves the safe ones
14
+ * cannot reach it. Separating a destructive action by position does not work,
15
+ * because a hand already moving does not stop at a horizontal rule. Separating
16
+ * it by gesture does.
17
+ *
18
+ * "Everything else" is deliberate. A request nobody declared safe is handled
19
+ * exactly like one declared permanent. Only the wording softens, because in
20
+ * that case the component knows less and should not pretend otherwise.
21
+ *
22
+ * There are no timers here and there is no automatic decision. Dismissing is
23
+ * neither yes nor no, and the requests survive it.
24
+ */
25
+ export declare function ApprovalGate({ requests, onApprove, onDeny, onDismiss, title, className, }: ApprovalGateProps): import("react").JSX.Element;
26
+ //# sourceMappingURL=ApprovalGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApprovalGate.d.ts","sourceRoot":"","sources":["../../../src/components/ApprovalGate/ApprovalGate.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,SAAS,CAAA;AAEjE,OAAO,gBAAgB,CAAA;AACvB,OAAO,oBAAoB,CAAA;AA0F3B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,SAAS,EACT,MAAM,EACN,SAAS,EACT,KAAyB,EACzB,SAAS,GACV,EAAE,iBAAiB,+BAuQnB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Approving something that cannot be undone, by holding rather than clicking.
3
+ *
4
+ * A click is the same motion that cleared the reversible requests a moment
5
+ * earlier. A hold is not: it is continuous, it cannot be produced by a stray
6
+ * press, and it costs about as long as it takes to read the line you are
7
+ * agreeing to. Apple reaches for the same idea when an action must not happen
8
+ * by accident, which is why Apple Pay wants a double click of the side button
9
+ * rather than a tap.
10
+ *
11
+ * Timings are deliberate and come from two different rules:
12
+ *
13
+ * - The fill takes 1.2 seconds and is **linear**, because it maps to real
14
+ * elapsed time and easing would misrepresent how much is left. This is the
15
+ * person deciding, so it is slow. A reflex press is around 150ms, so a second
16
+ * is already past anything that can happen by accident, and longer than that
17
+ * buys no safety and only spends patience.
18
+ * - The release takes 200ms and eases out. This is the system responding, so
19
+ * it is fast. Everything else in this library stays under 300ms for the same
20
+ * reason.
21
+ *
22
+ * Nobody is required to hold anything:
23
+ *
24
+ * - **Keyboard** confirms on Enter or Space, with no hold. Reaching this
25
+ * control already took a deliberate act, because the request had to be
26
+ * opened first, so the separation still holds without demanding that someone
27
+ * keep a key down at all. The button still reads "Hold to approve", because
28
+ * changing the words per input device means the label is wrong for anyone who
29
+ * uses both, which is most people.
30
+ * - **Reduce Motion** still holds. The setting asks for less movement, not for
31
+ * less care, and turning the hold into a click there would hand the one
32
+ * population most likely to have it switched on the exact reflex this control
33
+ * exists to defeat. So the fill stops animating and a timer runs instead: the
34
+ * same 1.2 seconds, the same label, nothing moving.
35
+ */
36
+ export declare function HoldToConfirm({ onConfirm, consequence, describedBy, }: {
37
+ onConfirm: () => void;
38
+ /** What approving this actually does. Goes into the control's spoken name. */
39
+ consequence: string;
40
+ /** The id of the line saying this cannot be undone. */
41
+ describedBy?: string;
42
+ }): import("react").JSX.Element;
43
+ //# sourceMappingURL=HoldToConfirm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HoldToConfirm.d.ts","sourceRoot":"","sources":["../../../src/components/ApprovalGate/HoldToConfirm.tsx"],"names":[],"mappings":"AA2BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,+BA4HA"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Whether an action can be undone.
3
+ *
4
+ * Three values, not two, because "nobody has said" is a different claim from
5
+ * "this is permanent", and printing the second when you only know the first is
6
+ * a lie the reader will eventually catch. Once they catch a gate overstating
7
+ * danger on something harmless, they discount it everywhere, including on the
8
+ * one that really was a deletion. That is the failure this component exists to
9
+ * prevent, arrived at by the back door.
10
+ *
11
+ * Apple never conflates these either. Every permission API they own has a
12
+ * `notDetermined` case sitting in front of `denied`.
13
+ *
14
+ * Only `true` is treated as safe. `false` and `'unknown'` behave identically:
15
+ * never batched, no approve control until opened, held rather than clicked.
16
+ * The behaviour does not move. Only the sentence does.
17
+ */
18
+ export type Reversibility = boolean | 'unknown';
19
+ export interface ApprovalRequest {
20
+ /** Your own identifier. Handed back to you when the person answers. */
21
+ id: string;
22
+ /**
23
+ * What happens if this is approved, in plain words, from the point of view of
24
+ * the person reading it. "Deletes 3 files in src", not "rm -rf src".
25
+ *
26
+ * This leads, because people approve commands they have not actually parsed.
27
+ */
28
+ consequence: string;
29
+ /** The literal command or path. Shown smaller, underneath. Optional. */
30
+ detail?: string;
31
+ /**
32
+ * Whether this can be undone. Required on purpose.
33
+ *
34
+ * Required rather than optional so that a developer has to confront the
35
+ * question, and `'unknown'` exists so they can answer it honestly when they
36
+ * genuinely do not know. Anything that is not explicitly `true` is treated as
37
+ * irreversible, so an omission still fails towards asking.
38
+ */
39
+ reversible: Reversibility;
40
+ }
41
+ export interface ApprovalGateProps {
42
+ /** Everything currently waiting on a decision. Empty renders nothing. */
43
+ requests: ApprovalRequest[];
44
+ /** Called with the ids being approved. Reversible ones may arrive together. */
45
+ onApprove: (ids: string[]) => void;
46
+ /** Called with the ids being refused. */
47
+ onDeny: (ids: string[]) => void;
48
+ /**
49
+ * Called when the person puts this away without deciding.
50
+ *
51
+ * Dismissing is not approval and not refusal. The requests are still
52
+ * pending afterwards. Leave this out and the gate cannot be dismissed.
53
+ */
54
+ onDismiss?: () => void;
55
+ /**
56
+ * Heading. Defaults to "Waiting for you".
57
+ *
58
+ * Pass `false` to render no heading row at all, for when the gate sits inline
59
+ * in a conversation and is plainly the only thing asking. The section is
60
+ * still named for assistive technology.
61
+ */
62
+ title?: string | false;
63
+ /** Your own class, for spacing and positioning from the outside. */
64
+ className?: string;
65
+ }
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/ApprovalGate/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,CAAA;AAE/C,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAA;IACV;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;;OAOG;IACH,UAAU,EAAE,aAAa,CAAA;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,+EAA+E;IAC/E,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAClC,yCAAyC;IACzC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAC/B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IACtB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
@@ -0,0 +1,18 @@
1
+ import type { SessionStripProps } from './types';
2
+ import '../sr-only.css';
3
+ import './SessionStrip.css';
4
+ /**
5
+ * Many parallel sessions, read at a glance.
6
+ *
7
+ * Two rules hold this together. Sessions that need a person come first and are
8
+ * never folded away, however many there are. Everything else is allowed to
9
+ * collapse into a single line of counts, because a list of twelve calm rows is
10
+ * telemetry, and nobody reads telemetry.
11
+ *
12
+ * Rows do not animate when they reorder. A session that starts needing you
13
+ * jumps to the top immediately, with no transition, matching the menu bar.
14
+ * Motion belongs to a single dot describing itself, not to a list rearranging
15
+ * under someone's eyes.
16
+ */
17
+ export declare function SessionStrip({ sessions, maxQuiet, onSelect, label, className, }: SessionStripProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=SessionStrip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionStrip.d.ts","sourceRoot":"","sources":["../../../src/components/SessionStrip/SessionStrip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,iBAAiB,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,gBAAgB,CAAA;AACvB,OAAO,oBAAoB,CAAA;AAoD3B;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAY,EACZ,QAAQ,EACR,KAAkB,EAClB,SAAS,GACV,EAAE,iBAAiB,+BAwFnB"}
@@ -0,0 +1,31 @@
1
+ import type { SessionState } from '../StatusIndicator/types';
2
+ export interface Session {
3
+ /** Your own identifier. Handed back by onSelect. */
4
+ id: string;
5
+ /** What the session is called. */
6
+ name: string;
7
+ state: SessionState;
8
+ /** How long it has been in this state, already formatted. "2m", "1h". */
9
+ elapsed?: string;
10
+ }
11
+ export interface SessionStripProps {
12
+ sessions: Session[];
13
+ /**
14
+ * How many quiet sessions to show as rows before folding the rest into the
15
+ * summary line. Defaults to 4.
16
+ *
17
+ * Sessions that need a person are never folded, no matter what this is set
18
+ * to. The strip compresses the quiet, never the actionable.
19
+ */
20
+ maxQuiet?: number;
21
+ /**
22
+ * Makes each row selectable. Leave it out and the strip is a read out with
23
+ * nothing to click, which is the default on purpose.
24
+ */
25
+ onSelect?: (id: string) => void;
26
+ /** Accessible name for the list. Defaults to "Sessions". */
27
+ label?: string;
28
+ /** Your own class, for spacing and positioning from the outside. */
29
+ className?: string;
30
+ }
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/SessionStrip/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,MAAM,WAAW,OAAO;IACtB,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAA;IACV,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,YAAY,CAAA;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
@@ -0,0 +1,17 @@
1
+ import type { StatusIndicatorProps } from './types';
2
+ import '../sr-only.css';
3
+ import './StatusIndicator.css';
4
+ /**
5
+ * One session's state, as a dot with an optional label.
6
+ *
7
+ * Motion means state, colour means exception. Only `running` moves, because
8
+ * only `running` is the case of being alive without progressing, and a breath
9
+ * is what that looks like. `error` is told apart by shape rather than hue, so
10
+ * it still reads for anyone who cannot separate red from amber.
11
+ *
12
+ * This component reports. It is never a control. It has no click handler and
13
+ * takes no children on purpose: the moment a status can be clicked it has
14
+ * become an approval, and approvals are ApprovalGate's job.
15
+ */
16
+ export declare function StatusIndicator({ state, label, showLabel, size, className, }: StatusIndicatorProps): import("react").JSX.Element;
17
+ //# sourceMappingURL=StatusIndicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/StatusIndicator/StatusIndicator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,oBAAoB,EAAE,MAAM,SAAS,CAAA;AACjE,OAAO,gBAAgB,CAAA;AACvB,OAAO,uBAAuB,CAAA;AAkB9B;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,KAAK,EACL,SAAiB,EACjB,IAAW,EACX,SAAS,GACV,EAAE,oBAAoB,+BAkBtB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The states a session can be in.
3
+ *
4
+ * These are not invented for a component library. They are the states that
5
+ * survived daily use of Session Indicator, a macOS app that watches real agent
6
+ * sessions. The union below is the whole model: TypeScript refuses any value
7
+ * that is not on this list.
8
+ */
9
+ export type SessionState =
10
+ /** Open, at the prompt, nothing happening. */
11
+ 'idle'
12
+ /** The agent itself is producing. Progress is happening right now. */
13
+ | 'working'
14
+ /** A command the agent started is still going. Alive, but not progressing. */
15
+ | 'running'
16
+ /** Stopped, and it cannot continue until a person answers. */
17
+ | 'needsYou'
18
+ /** Something failed. */
19
+ | 'error'
20
+ /** Finished. */
21
+ | 'done';
22
+ export interface StatusIndicatorProps {
23
+ /** Which state to show. The only required prop. */
24
+ state: SessionState;
25
+ /**
26
+ * Replaces the built in wording. Use it for other languages, or when your
27
+ * product says "Paused" where this says "Idle".
28
+ */
29
+ label?: string;
30
+ /**
31
+ * Show the label next to the dot. Defaults to false.
32
+ *
33
+ * This controls whether the label is *visible*, not whether it exists. The
34
+ * text is always in the page for screen readers, because a bare coloured dot
35
+ * communicates nothing to someone who cannot see it.
36
+ */
37
+ showLabel?: boolean;
38
+ /** Dot size. Named sizes only, see the note in the component. */
39
+ size?: 'sm' | 'md' | 'lg';
40
+ /** Your own class, for spacing and positioning from the outside. */
41
+ className?: string;
42
+ }
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/StatusIndicator/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY;AACtB,8CAA8C;AAC5C,MAAM;AACR,sEAAsE;GACpE,SAAS;AACX,8EAA8E;GAC5E,SAAS;AACX,8DAA8D;GAC5D,UAAU;AACZ,wBAAwB;GACtB,OAAO;AACT,gBAAgB;GACd,MAAM,CAAA;AAEV,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,KAAK,EAAE,YAAY,CAAA;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,iEAAiE;IACjE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
@@ -0,0 +1,9 @@
1
+ export { StatusIndicator } from './components/StatusIndicator/StatusIndicator';
2
+ export type { SessionState, StatusIndicatorProps } from './components/StatusIndicator/types';
3
+ export { ApprovalGate } from './components/ApprovalGate/ApprovalGate';
4
+ export type { ApprovalGateProps, ApprovalRequest, Reversibility, } from './components/ApprovalGate/types';
5
+ export { SessionStrip } from './components/SessionStrip/SessionStrip';
6
+ export type { Session, SessionStripProps } from './components/SessionStrip/types';
7
+ export { ActivityTrail } from './components/ActivityTrail/ActivityTrail';
8
+ export type { Activity, ActivityKind, ActivityTrailProps } from './components/ActivityTrail/types';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAA;AAC9E,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAE5F,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AACrE,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,iCAAiC,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AACrE,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAEjF,OAAO,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AACxE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA"}