@marketrix.ai/widget 4.0.135 → 4.0.137

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.
@@ -6,7 +6,11 @@
6
6
  * shell's toolbar button fills with a share toggle, and the composer textarea ref. `MODES` pairs the
7
7
  * mode chips' display order with the tenant setting enabling each, so the composer offers only what
8
8
  * the workspace turned on. `ChatView` owns the draft text; `handleSendMessage` posts the turn and
9
- * `handleModeChange` announces a mode switch in the transcript before flipping state.
9
+ * `handleModeChange` announces a mode switch in the transcript before flipping state. A POST failure
10
+ * restores the composed text to the (now-cleared) composer — `messageDispatch`'s resolved `false` —
11
+ * unless the visitor already started typing a new message in the meantime, so a resend is one tap on
12
+ * Send rather than a retype, and the failed turn's own bubble (added optimistically, unconditionally)
13
+ * still shows what was said either way.
10
14
  *
11
15
  * The composer is locked both while a reply is outstanding and while a screen-access request is open,
12
16
  * since a second turn queued behind an unanswered permission card has nowhere to land. `use_screenshare`
@@ -8,15 +8,30 @@
8
8
  * defers updaters and loses the effects captured in them, so tool calls never execute. `messageDispatch`
9
9
  * POSTs fire-and-forget: the reply arrives over SSE, so only a POST failure resolves the placeholder
10
10
  * locally, and a stale-reply watchdog keyed on id AND part count re-arms on every progress line.
11
+ * `messageDispatch` resolves `false` on that same POST failure (`true` otherwise, including preview
12
+ * mode) so a caller holding the composed text — `ChatView`'s composer — can restore it instead of the
13
+ * visitor having to retype a message the widget already dropped from the input box.
11
14
  *
12
15
  * The stream effect subscribes to the `StreamClient` singleton: `handleMessage` does the bookkeeping the
13
- * pure reducer cannot hold (`tool_call_id` dedupe in a bounded set, cleared on a terminal status), then
14
- * each effect runs its browser tool, stamps progress, replies `tool/response` and only then fires
15
- * `afterResponseAttempt`. `handleError` converts only a `StreamGaveUpError` into a transport failure, as a
16
- * retriable blip settles when the reply lands on the reconnected stream. `stopTask` sends `chat/stop`,
17
- * which carries no task id. Every failure on the tool and stop paths reaches `uiActions.setError` as well
18
- * as the console: an undelivered `tool/response` leaves the agent waiting on a reply that never comes, so
19
- * the run stalls with nothing on screen unless the visitor is told, and `do` mode may still be clicking.
16
+ * pure reducer cannot hold (`tool_call_id` dedupe in a bounded set, cleared on a terminal status; the
17
+ * mirror-shaped `respondedRequestIds` below), then each effect runs its browser tool, stamps progress,
18
+ * replies `tool/response` and only then fires `afterResponseAttempt`. `handleError` converts only a
19
+ * `StreamGaveUpError` into a transport failure, as a retriable blip settles when the reply lands on the
20
+ * reconnected stream. `stopTask` sends `chat/stop`, which carries no task id. Every failure on the tool
21
+ * and stop paths reaches `uiActions.setError` as well as the console: an undelivered `tool/response`
22
+ * leaves the agent waiting on a reply that never comes, so the run stalls with nothing on screen unless
23
+ * the visitor is told, and `do` mode may still be clicking.
24
+ *
25
+ * `respondedRequestIds` closes the other half of the reconnect-replay contract `StreamClient.ts`
26
+ * documents (the api replays a chat_id's whole turn history, not just the tail a client missed):
27
+ * `reduceText`'s own exact-repeat guard (`sseReducer.ts`) only catches an identical FINAL
28
+ * `chat/response` replayed after the answer already closed, because it only ever compares against the
29
+ * message's LAST part. A replayed `chat/delta` fails that same-string check (it's a partial fragment,
30
+ * not the full closed text) and would otherwise be appended as a brand-new, visibly duplicated text
31
+ * segment ahead of the closing response that then only overwrites the one it just added — leaving two
32
+ * copies of the same answer on screen. Scoped by `request_id`, not globally, so an unrelated later
33
+ * turn's genuinely new `chat/delta`/`chat/response` is untouched; marked closed only once, on the
34
+ * request's own terminal `chat/response`, since a delta is by definition still open.
20
35
  *
21
36
  * `lastStreamErrorRef`/`currentErrorRef` close the loop `handleError` alone leaves open: `StreamClient`
22
37
  * calls `onError` on every failed dial, not only a terminal give-up, so the visitor sees "Stream
@@ -43,7 +58,7 @@ interface ChatActions {
43
58
  removeMessage: (messageId: string) => void;
44
59
  setMessages: (messages: ChatMessage[]) => void;
45
60
  clearMessages: () => void;
46
- messageDispatch: (content: string, mode?: InstructionType, skipUserMessage?: boolean) => Promise<void>;
61
+ messageDispatch: (content: string, mode?: InstructionType, skipUserMessage?: boolean) => Promise<boolean>;
47
62
  }
48
63
  interface TaskActions {
49
64
  resetTask: () => void;
@@ -21,7 +21,7 @@ export declare const useWidget: () => {
21
21
  removeMessage: (messageId: string) => void;
22
22
  setMessages: (messages: import("..").ChatMessage[]) => void;
23
23
  clearMessages: () => void;
24
- messageDispatch: (content: string, mode?: import("..").InstructionType, skipUserMessage?: boolean) => Promise<void>;
24
+ messageDispatch: (content: string, mode?: import("..").InstructionType, skipUserMessage?: boolean) => Promise<boolean>;
25
25
  resetTask: () => void;
26
26
  stopTask: () => Promise<void>;
27
27
  setActiveView: (view: import("../types").WidgetView) => void;
@@ -1,15 +1,8 @@
1
1
  /**
2
- * `ActivityMetadataByType` the strict, per-`ActivityLogType` shape of `activity_log.metadata`, the
3
- * one write-time gate `models/columnSchemas.ts` registers against the column. Moved out of that file
4
- * so `contracts/entities.ts` can import it to type `ActivityLogEntitySchema.metadata` precisely
5
- * (a union of every branch) instead of the open `.passthrough()` it carried before — deriving from a
6
- * registry that lived inside `models/columnSchemas.ts`, which itself imports from `entities.ts`,
7
- * would have cycled.
2
+ * The strict shape of an activity log row's `metadata` column, one schema per activity type.
8
3
  *
9
- * `slack_command`'s `status` imports `SlackCommandLogStatusSchema` from `./activityLogVocabulary`
10
- * the one home `tests/unit/contractEnumHomes.test.ts` requires for that value set a dependency-free
11
- * leaf, so this registry (and everything importing it through `entities.ts`) never pulls in
12
- * `contracts/slack.ts`'s `@orpc/contract` import.
4
+ * `ActivityMetadataByType` maps each `ActivityLogType` to its metadata shape; `contracts/entities.ts`
5
+ * and `models/columnSchemas.ts` both read it, which is why it lives in its own file rather than either.
13
6
  */
14
7
  import { z } from 'zod';
15
8
  export declare const ActivityMetadataByType: {
@@ -1,15 +1,7 @@
1
1
  /**
2
- * The four closed vocabularies an activity-log row's shape is keyed on `type` (what happened),
3
- * the two enums that ride INSIDE some per-type metadata shapes (`ApplicationTypeSchema`,
4
- * `WidgetTypeSchema`), and `SlackCommandLogStatusSchema` (the `slack_command` metadata's `status`,
5
- * and `contracts/slack.ts`'s command-log entity status — one home per `tests/unit/contractEnumHomes.test.ts`).
6
- * Split out of `contracts/entities.ts` into this dependency-free leaf — no `@orpc/contract` import —
7
- * so `contracts/activityLogMetadata.ts`'s per-type registry can import them without cycling back
8
- * through `entities.ts`, which needs the registry to type `ActivityLogEntitySchema.metadata`
9
- * precisely instead of `.passthrough()`, and so an audience that never touches oRPC contract routes
10
- * (the internal/monitor mirror) doesn't pull `@orpc/contract` in through the metadata registry.
11
- * `entities.ts` re-exports the first three (unchanged); `SlackCommandLogStatusSchema` is new here and
12
- * has no `entities.ts` re-export since nothing imported it from there before.
2
+ * Closed vocabularies used across activity log rows: application and widget types, every activity
3
+ * `type` value, and Slack command log status. Kept dependency-free so other contract files can use
4
+ * them without pulling in unrelated imports.
13
5
  */
14
6
  import { z } from 'zod';
15
7
  export declare const ApplicationTypeSchema: z.ZodEnum<{
@@ -1,31 +1,10 @@
1
1
  /**
2
- * Cross-domain wire primitives shared by every audience this file is mirrored WHOLE into the widget
3
- * closure, so any shape it exports republishes the widget SDK regardless of which audience actually
4
- * reads it.
5
- * - `unionOfRecord` builds a plain (non-discriminated) union of every variant in a
6
- * `{ <discriminant value>: ZodType }` map the shape `TriggerSourceConfigSchemas`/
7
- * `WorkflowActionTargetConfigSchemas` are declared in, and the registry (`models/columnSchemas.ts`)
8
- * keys by the same discriminant separately. Typed off the map's own value type rather than a bare
9
- * `z.ZodType`, whose inferred output is `unknown` and would erase every variant's real shape from the
10
- * union.
11
- * - `ToolCallRecordSchema.params`/`.result` stay `z.record(z.string(), z.unknown())` ON PURPOSE —
12
- * `models/columnSchemas.ts`'s header lists a tool call's own arguments and result as one of the few
13
- * open boundaries kept opaque deliberately, since a tool's shape varies per tool name with no closed
14
- * vocabulary this file (or the widget/app readers of `simulation_step.tool_calls`) can type against.
15
- * - `SessionStateSchema` is `simulation.session_state` (Browserbase cookies + localStorage snapshot).
16
- * Lives here rather than `models/columnSchemas.ts`, which imports FROM
17
- * `contracts/foundationEntities.ts` — a leaf-shaped schema this file already is one, so
18
- * `contracts/foundationEntities.ts` can type `SimulationEntitySchema`'s own `session_state` field
19
- * with it without cycling back through `columnSchemas.ts`. Its `cookies` field is the other
20
- * deliberately-open boundary from that same registry header: a browser cookie as the browser itself
21
- * reports it, no closed shape to narrow to.
22
- * - `GraphNodeSummarySchema` is the whole-graph tier — `applicationGraphGet`/`simulationGraphGet` load
23
- * nodes with `readGraph`, which always resolves sections to `[]` for speed; a node's real sections
24
- * are a lazy drill-in fetched one at a time by `graphNodeSectionsGet` (its own
25
- * `GraphSectionSchema`-shaped output), so this tier never carries them. `sequence_ids` is DROPPED
26
- * (not just unselected) — the stored `graph.graph_nodes` column stays for the agent's own write-side
27
- * dedupe, but no app/widget graph or heatmap component ever read the wire field, and this file's
28
- * widget-closure membership means dropping it republishes the widget.
2
+ * Wire primitives shared across every domain: id/pagination shapes, tool call and browser session
3
+ * state, and the application knowledge graph.
4
+ *
5
+ * Exports helpers like `paginatedListOf`/`unionOfRecord`, the id and pagination input schemas, and the
6
+ * graph and session-state entity schemas. This file mirrors whole into the widget SDK, so any shape
7
+ * added here reaches the widget even if nothing else changes.
29
8
  */
30
9
  import { z } from 'zod';
31
10
  export declare const EntityStatusSchema: z.ZodEnum<{
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Entity schemas for users, workspaces, applications, widgets and activity log rows — the shapes most
3
+ * other contracts build on.
4
+ *
5
+ * Exports the full entity schema and a narrower read/summary variant for each, plus the activity log's
6
+ * per-type metadata union. `ApplicationEntitySchema`'s `skill_distillation_status`/`_error` track the
7
+ * last skill-distillation attempt for that application, since a distilled skill is written directly with
8
+ * no separate draft row. `ApplicationReadSchema` never returns `password`; `WidgetPublicSchema` never
9
+ * returns the widget's own auth credentials, since an unauthenticated visitor's browser is the caller.
10
+ */
1
11
  import { z } from 'zod';
2
12
  import { type ActivityLogType, ActivityLogTypeSchema, type ApplicationType, ApplicationTypeSchema, type WidgetType, WidgetTypeSchema } from './activityLogVocabulary';
3
13
  export { ActivityLogTypeSchema, ApplicationTypeSchema, WidgetTypeSchema };
@@ -9,7 +19,6 @@ export declare const WorkspacePackageSchema: z.ZodEnum<{
9
19
  enterprise: "enterprise";
10
20
  }>;
11
21
  export type PlanTier = z.infer<typeof WorkspacePackageSchema>;
12
- /** The plan a Billing Cycle was opened on. `legacy_unknown` is pre-ledger history, which no plan sells. */
13
22
  export declare const PlanSnapshotSchema: z.ZodEnum<{
14
23
  free: "free";
15
24
  startup: "startup";
@@ -36,11 +45,6 @@ export declare const InstructionTypeSchema: z.ZodEnum<{
36
45
  do: "do";
37
46
  }>;
38
47
  export type InstructionType = z.infer<typeof InstructionTypeSchema>;
39
- /**
40
- * The workspace role vocabulary — `admin` administers, `member` does not. One spelling everywhere:
41
- * DB enum, this contract, the dashboard, and the WorkOS role slug. Declared here (not `workspace.ts`)
42
- * because `UserEntitySchema` needs it and `workspace.ts` already imports from this file.
43
- */
44
48
  export declare const WorkspaceMemberRoleSchema: z.ZodEnum<{
45
49
  admin: "admin";
46
50
  member: "member";
@@ -135,6 +139,12 @@ export declare const WorkspaceSummarySchema: z.ZodObject<{
135
139
  notify_all_members_on_question: z.ZodBoolean;
136
140
  }, z.core.$strip>;
137
141
  export type WorkspaceSummary = z.infer<typeof WorkspaceSummarySchema>;
142
+ export declare const ApplicationSkillDistillationStatusSchema: z.ZodEnum<{
143
+ failed: "failed";
144
+ idle: "idle";
145
+ pending: "pending";
146
+ }>;
147
+ export type ApplicationSkillDistillationStatus = z.infer<typeof ApplicationSkillDistillationStatusSchema>;
138
148
  export declare const ApplicationEntitySchema: z.ZodObject<{
139
149
  id: z.ZodNumber;
140
150
  created_at: z.ZodCoercedDate<unknown>;
@@ -150,9 +160,14 @@ export declare const ApplicationEntitySchema: z.ZodObject<{
150
160
  username: z.ZodNullable<z.ZodString>;
151
161
  password: z.ZodNullable<z.ZodString>;
152
162
  allowed_domains: z.ZodArray<z.ZodString>;
163
+ skill_distillation_status: z.ZodEnum<{
164
+ failed: "failed";
165
+ idle: "idle";
166
+ pending: "pending";
167
+ }>;
168
+ skill_distillation_error: z.ZodNullable<z.ZodString>;
153
169
  }, z.core.$strip>;
154
170
  export type ApplicationData = z.infer<typeof ApplicationEntitySchema>;
155
- /** Used for all API responses; password is write-only and never returned to clients. */
156
171
  export declare const ApplicationReadSchema: z.ZodObject<{
157
172
  id: z.ZodNumber;
158
173
  created_at: z.ZodCoercedDate<unknown>;
@@ -167,6 +182,12 @@ export declare const ApplicationReadSchema: z.ZodObject<{
167
182
  workspace_id: z.ZodNumber;
168
183
  username: z.ZodNullable<z.ZodString>;
169
184
  allowed_domains: z.ZodArray<z.ZodString>;
185
+ skill_distillation_status: z.ZodEnum<{
186
+ failed: "failed";
187
+ idle: "idle";
188
+ pending: "pending";
189
+ }>;
190
+ skill_distillation_error: z.ZodNullable<z.ZodString>;
170
191
  }, z.core.$strip>;
171
192
  export type ApplicationReadData = z.infer<typeof ApplicationReadSchema>;
172
193
  export declare const WidgetChipSchema: z.ZodObject<{
@@ -1,3 +1,11 @@
1
+ /**
2
+ * The support widget: its settings, its public boot lookup, and the SSE event/command vocabulary that
3
+ * drives a live chat session.
4
+ *
5
+ * Exports the widget entity and create/update schemas, `WidgetEventSchema`/`WidgetCommandSchema`, and
6
+ * every widget CRUD and streaming procedure. `widgetPublicSearch` is the widget's own credentialed boot
7
+ * call and never returns the credentials that authenticated it.
8
+ */
1
9
  import { z } from 'zod';
2
10
  export declare const WidgetCreateSchema: z.ZodObject<{
3
11
  status: z.ZodOptional<z.ZodEnum<{
@@ -101,7 +109,6 @@ export declare const WidgetUpdateSchema: z.ZodObject<{
101
109
  }, z.core.$strip>>;
102
110
  }, z.core.$strip>;
103
111
  export type WidgetUpdateData = z.infer<typeof WidgetUpdateSchema>;
104
- /** Server → Widget events. */
105
112
  export declare const WidgetEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
106
113
  type: z.ZodLiteral<"registered">;
107
114
  chat_id: z.ZodString;
@@ -141,7 +148,6 @@ export declare const WidgetEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
141
148
  explanation: z.ZodOptional<z.ZodString>;
142
149
  }, z.core.$strip>], "type">;
143
150
  export type WidgetEvent = z.infer<typeof WidgetEventSchema>;
144
- /** Widget → Server commands. */
145
151
  export declare const WidgetCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
146
152
  type: z.ZodLiteral<"chat/tell">;
147
153
  request_id: z.ZodString;
@@ -5,7 +5,12 @@
5
5
  * browser-local key shares via `scopedKey`, so the chat-context, drag-position and resize keys all
6
6
  * partition by tenant identically. `readLocal`/`writeLocal` are the only `localStorage` access in `src/`;
7
7
  * a host page can deny storage outright (third-party cookies off, sandboxed iframe), so both degrade to
8
- * a warn and the widget keeps working unpersisted.
8
+ * memory and the widget keeps working unpersisted. `updateContext` calls `writeLocal` on every UI-state
9
+ * change (a drag, a resize, every chat message), so without `warnOnce` a denied host page would spam one
10
+ * console line per write for the visitor's whole session; a single warn on first denial says everything
11
+ * a customer's console needs. `warnOnce` is keyed by read vs. write since `readLocal` also fires once on
12
+ * its own (`loadContext` at module init, before any write) and both should still surface if a page
13
+ * somehow denies one and not the other.
9
14
  *
10
15
  * `loadContext` merges one parsed key over `DEFAULT_CONTEXT`, so an older widget version's payload reads
11
16
  * as incomplete rather than corrupt, and discards anything past `CONTEXT_EXPIRY_MS` (7 days).
@@ -55,6 +60,10 @@ type MarketrixChatContext = Omit<ChatSnapshot, 'messages'> & {
55
60
  };
56
61
  export declare function tenantScope(config: MarketrixConfig): string;
57
62
  export declare function scopedKey(name: string, config: MarketrixConfig): string;
63
+ /** Test-only: `warned` is module-level so a "denies storage, keeps working" proof isn't the last test in
64
+ * the file to touch a denied `Storage.prototype`, and a later test in the same `bun test` process (one
65
+ * process per file, not per test) would otherwise see zero warns instead of one. Never called from `src/`. */
66
+ export declare function resetStorageWarningsForTests(): void;
58
67
  export declare function readLocal(key: string): string | null;
59
68
  export declare function writeLocal(key: string, value: string): void;
60
69
  declare class StorageService {
@@ -6,7 +6,10 @@
6
6
  *
7
7
  * `open` is the transport, `registered` is the chat: only the latter can carry a reply, so `isConnected` reads
8
8
  * `registered` and nothing waits on `open`. Backoff counters reset only on `registered` — resetting at `open` would
9
- * defeat the max-attempts cap if registration never lands and the stream flaps open→closed. Tabs share the
9
+ * defeat the max-attempts cap if registration never lands and the stream flaps open→closed. `scheduleReconnect`'s
10
+ * EQUAL JITTER (the doubling delay's own second half, chosen uniformly) keeps every dial within the documented
11
+ * schedule's bound while stopping every tab across every open customer page from redialing on the exact same
12
+ * clock tick after a shared outage — a thundering herd the deterministic schedule alone cannot prevent. Tabs share the
10
13
  * localStorage chat id, so the server keys SSE by (chat_id, tab_id) and `tabId` stops tabs evicting each other's
11
14
  * stream. Credentials are read at connect time, not captured at init, so a reconnect after `updateMarketrixConfig`
12
15
  * dials with the current ones.