@mlpal/yodex-ui 0.2.0 → 0.2.2

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.
package/README.md CHANGED
@@ -34,7 +34,12 @@ function Chat({ url, token }: { url: string; token?: string }) {
34
34
  prompts, run status, usage, session and model lists.
35
35
  - The reducer and client are framework-free (`YodexClient`, `reduce`, `applyEvent`) for hosts that
36
36
  are not React.
37
- - Styling: Tailwind utilities compiled against the Obsidian Intelligence token names
37
+ - Hosts that virtualize the transcript render `TranscriptRow` per item; `ApprovalPrompt` shows
38
+ `state.pendingApproval`; `PermissionPrompt` takes `onAllowSession` (the hook's `allowForSession`)
39
+ and opt-in `hotkeys` for the one prompt that should own `a`/`d`; the Composer steers a live run
40
+ through `onSteer` (the hook's `steer`) and takes `value`/`onChange`, `initialValue`, `hideAttach`,
41
+ `submitOnEnter` (defaults to keyboards only).
42
+ - Styling: `styles.css` carries only utilities (no preflight, no Tailwind theme on `:root`), compiled against the Obsidian Intelligence token names
38
43
  (`--background`, `--primary`, `--muted`, `--success`, `--warning`, `--info`, …). Every component
39
44
  works at 390 px width with 44 px touch targets.
40
45
  - `state.capabilities` mirrors `ready.capabilities` ("questions", "approval", "attachments",
@@ -0,0 +1,12 @@
1
+ import type { PendingPermission } from "./types";
2
+ /** The hook's session-scoped allow rules: a tool name plus, for commands, the exact command. */
3
+ export type SessionRule = string;
4
+ export declare function ruleFor(p: Pick<PendingPermission, "toolName" | "input">): SessionRule;
5
+ /** Whether a session rule may answer this ask. A safety-flagged ask is never a rule's to answer:
6
+ * the person sees it, every time. */
7
+ export declare function ruleAnswers(rules: ReadonlySet<SessionRule>, ask: Pick<PendingPermission, "toolName" | "input" | "safetyReason">): boolean;
8
+ export declare function revokeRules(rules: Set<SessionRule>, toolName: string): void;
9
+ /** A changed sessionId prop switches the view only when it names a session other than the one
10
+ * the hook already owns: a host passing back the id a new session was given on system.init is
11
+ * not a switch, and must not drop the running stream. */
12
+ export declare function switchesSession(nextProp: string | undefined, lastProp: string | undefined, owned: string | undefined): boolean;
@@ -59,6 +59,9 @@ export interface PendingPermission {
59
59
  sessionId: string;
60
60
  toolName: string;
61
61
  input: Record<string, unknown>;
62
+ /** Why the policy asked (yodex/2): a mode rule, or a HOP safety reason code. */
63
+ reason?: string;
64
+ safetyReason?: string;
62
65
  }
63
66
  export interface PendingQuestion {
64
67
  id: string;
@@ -26,6 +26,16 @@ export interface YodexSession {
26
26
  deny: (permissionId: string, reason?: string) => void;
27
27
  answer: (questionId: string, outcome: AskOutcome) => void;
28
28
  approve: (decision: "allow" | "deny", reason?: string) => void;
29
+ /** Allow this tool (and, for Bash, this exact command) without asking again while this hook lives.
30
+ * A later ask that carries a safety reason is never answered by such a rule: it is shown. */
31
+ allowForSession: (permissionId: string) => void;
32
+ /** Drop session rules: one tool's, or all of them, without remounting (no reconnect, no replay). */
33
+ revokeSessionRule: (toolName: string) => void;
34
+ clearSessionRules: () => void;
35
+ /** Steer a running session: the text reaches the agent at its next turn boundary. */
36
+ steer: (text: string) => void;
37
+ /** What the run is waiting on, for the status row. */
38
+ waitingOn?: "permission" | "question";
29
39
  loadSession: (sessionId: string) => Promise<void>;
30
40
  refreshSessions: () => Promise<void>;
31
41
  refreshModels: () => Promise<void>;
@@ -0,0 +1,7 @@
1
+ import type { PendingApproval } from "@mlpal/harness-protocol";
2
+ /** The last run stopped at the HOP's safety edge (needs_approval). Approve to resume, or decline. */
3
+ export declare function ApprovalPrompt({ approval, onDecide, className }: {
4
+ approval: PendingApproval;
5
+ onDecide: (decision: "allow" | "deny", reason?: string) => void;
6
+ className?: string;
7
+ }): import("react").JSX.Element;
@@ -13,9 +13,18 @@ export interface HopOption {
13
13
  }
14
14
  export interface ComposerProps {
15
15
  onSubmit: (text: string, attachments: ComposerAttachment[]) => void;
16
- /** While a run is live the send button becomes Stop. */
16
+ /** While a run is live the send button becomes Stop; with `onSteer`, typed text sends as a steer instead. */
17
17
  running?: boolean;
18
18
  onStop?: () => void;
19
+ onSteer?: (text: string) => void;
20
+ /** Controlled text (with onChange), or `initialValue` to prefill once. */
21
+ value?: string;
22
+ onChange?: (text: string) => void;
23
+ initialValue?: string;
24
+ /** Enter sends on keyboards; on touch devices the send button does (Shift+Enter always breaks a line).
25
+ * Force one behaviour with true / false. */
26
+ submitOnEnter?: boolean;
27
+ hideAttach?: boolean;
19
28
  disabled?: boolean;
20
29
  /** Shown as a placeholder reason while disabled, e.g. "Workspace is waking up". */
21
30
  disabledReason?: string;
@@ -29,9 +38,4 @@ export interface ComposerProps {
29
38
  className?: string;
30
39
  autoFocus?: boolean;
31
40
  }
32
- /**
33
- * The input: a growing textarea, Enter sends and Shift+Enter breaks a line, images paste or
34
- * drop in as attachments, a HOP picker slot, Stop while running, and an outside-controlled
35
- * disabled state for a sleeping workspace.
36
- */
37
- export declare function Composer({ onSubmit, running, onStop, disabled, disabledReason, placeholder, hopOptions, hop, onHopChange, leading, maxAttachmentBytes, className, autoFocus }: ComposerProps): import("react").JSX.Element;
41
+ export declare function Composer({ onSubmit, running, onStop, onSteer, value, onChange, initialValue, submitOnEnter, hideAttach, disabled, disabledReason, placeholder, hopOptions, hop, onHopChange, leading, maxAttachmentBytes, className, autoFocus }: ComposerProps): import("react").JSX.Element;
@@ -1,9 +1,14 @@
1
1
  import type { PendingPermission } from "../client/types";
2
2
  export declare function permissionSummary(p: PendingPermission): string;
3
- /** The engine asks before a tool runs. Allow / Deny, with `a` / `d` as keys while focused. */
4
- export declare function PermissionPrompt({ request, onAllow, onDeny, className }: {
3
+ export interface PermissionPromptProps {
5
4
  request: PendingPermission;
6
5
  onAllow: (id: string) => void;
7
6
  onDeny: (id: string, reason?: string) => void;
7
+ /** Shown as a third choice when given (the hook's allowForSession). */
8
+ onAllowSession?: (id: string) => void;
9
+ /** `a` / `d` answer this prompt while focus is outside an input. Enable on one prompt at a time. */
10
+ hotkeys?: boolean;
8
11
  className?: string;
9
- }): import("react").JSX.Element;
12
+ }
13
+ /** The engine asks before a tool runs: Allow, Allow for this session, or Deny with an optional reason. */
14
+ export declare function PermissionPrompt({ request, onAllow, onDeny, onAllowSession, hotkeys, className }: PermissionPromptProps): import("react").JSX.Element;
@@ -4,6 +4,8 @@ export interface RunStatusProps {
4
4
  running: boolean;
5
5
  startedAt?: number;
6
6
  activity?: string;
7
+ /** The run is waiting on the person; shown instead of the activity. */
8
+ waitingOn?: "permission" | "question";
7
9
  lastResult?: ResultEvent;
8
10
  usageTotal?: Usage;
9
11
  connection?: ConnectionState;
@@ -14,4 +16,4 @@ export interface RunStatusProps {
14
16
  now?: () => number;
15
17
  }
16
18
  /** One row: what the agent is doing with elapsed time while running; the result line after. */
17
- export declare function RunStatus({ running, startedAt, activity, lastResult, usageTotal, connection, onStop, onApprove, className, now }: RunStatusProps): import("react").JSX.Element | null;
19
+ export declare function RunStatus({ running, startedAt, activity, waitingOn, lastResult, usageTotal, connection, onStop, onApprove, className, now }: RunStatusProps): import("react").JSX.Element | null;
@@ -9,4 +9,11 @@ export interface TranscriptProps {
9
9
  emptyState?: React.ReactNode;
10
10
  className?: string;
11
11
  }
12
+ /** One transcript row. Exported so a host that virtualizes the list renders the same markup. */
13
+ export declare function TranscriptRow({ item, onOpenFile }: {
14
+ item: TranscriptItem;
15
+ onOpenFile?: (path: string) => void;
16
+ }): import("react").JSX.Element;
17
+ /** The whole transcript in a scrolling column. For long sessions a host may virtualize with
18
+ * TranscriptRow instead; announcements belong to RunStatus, not to the log itself. */
12
19
  export declare function Transcript({ items, autoScroll, onOpenFile, footer, emptyState, className }: TranscriptProps): import("react").JSX.Element;
package/dist/index.d.ts CHANGED
@@ -2,14 +2,16 @@ export * from "./client/types";
2
2
  export { YodexClient, type YodexClientOptions, type RunOptions, type SocketLike, type SocketFactory, type OutgoingMessage } from "./client/client";
3
3
  export { reduce, applyEvent, initialState, elapsedLabel, resultLabel, formatTokens } from "./client/reducer";
4
4
  export { useYodexSession, type UseYodexSessionOptions, type YodexSession, type Attachment } from "./client/useYodexSession";
5
- export { Transcript, type TranscriptProps } from "./components/Transcript";
5
+ export { Transcript, TranscriptRow, type TranscriptProps } from "./components/Transcript";
6
6
  export { AssistantMessage } from "./components/AssistantMessage";
7
7
  export { ToolCall, toolSummary } from "./components/ToolCall";
8
8
  export { DiffView, diffCounts } from "./components/DiffView";
9
- export { PermissionPrompt, permissionSummary } from "./components/PermissionPrompt";
9
+ export { PermissionPrompt, permissionSummary, type PermissionPromptProps } from "./components/PermissionPrompt";
10
+ export { ApprovalPrompt } from "./components/ApprovalPrompt";
10
11
  export { QuestionForm, buildOutcome, type QuestionDraft } from "./components/QuestionForm";
11
12
  export { PlanChecklist } from "./components/PlanChecklist";
12
13
  export { RunStatus } from "./components/RunStatus";
13
14
  export { SessionList, groupSessions, type SessionRow } from "./components/SessionList";
14
15
  export { Composer, type ComposerProps, type ComposerAttachment, type HopOption } from "./components/Composer";
15
16
  export { cn } from "./components/cn";
17
+ export { ruleFor, ruleAnswers, revokeRules, switchesSession } from "./client/rules";