dsh-daruma 0.1.2 → 0.1.4

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
@@ -20,6 +20,20 @@ daruma. daruma then:
20
20
  Channel health persists to `~/.dsh/daruma/channel-health.json`, so a tripped
21
21
  channel stays cooled-down across restarts.
22
22
 
23
+ ## Visible failover notices in the conversation
24
+
25
+ When a channel switch happens mid-turn, the web client renders a small
26
+ `daruma` row **inside the conversation flow** (anchored at the failover event,
27
+ right where it occurred): `mt::glm-5.3 failed (RATE_LIMIT) → trying
28
+ deepseek-official::deepseek-v4-flash`, with the budget usage
29
+ (`failover 2/3 · turn 3 step 1`) on hover.
30
+
31
+ This works through the host's plugin-extensible conversation engine: the
32
+ client registers a definition claiming `daruma/failover` session events plus a
33
+ keyed renderer for its node kind, mirroring how in-repo retries render. The
34
+ notice is **live-only** — out-of-repo session events are not yet persisted by
35
+ the harness, so rows do not survive a page reload or a resumed session.
36
+
23
37
  ## Install
24
38
 
25
39
  ```bash
@@ -0,0 +1,24 @@
1
+ /**
2
+ * FailoverNoticeRow — the daruma chat renderer for {@link FAILOVER_NOTICE_KIND}.
3
+ *
4
+ * A single quiet row inside the conversation flow telling the user that
5
+ * daruma caught a failed model request and switched the channel for the
6
+ * retry. Registered behind the host's keyed `conversation.chat.node` seat,
7
+ * so it renders exactly like an in-repo chat row (no DOM injection).
8
+ */
9
+ import { type CSSProperties } from 'react';
10
+ import { type FailoverNoticeData } from './failover-notice.ts';
11
+ /** Renderer props actually consumed (the keyed seat passes more; ignore it). */
12
+ export interface FailoverNoticeRowProps {
13
+ readonly node: {
14
+ readonly data: FailoverNoticeData;
15
+ };
16
+ readonly t: (key: string) => string;
17
+ }
18
+ /** Render one settled failover / give-up row, or nothing when unusable. */
19
+ export declare function FailoverNoticeRow({ node, t }: FailoverNoticeRowProps): import("react").DetailedReactHTMLElement<{
20
+ 'data-daruma-notice': string;
21
+ role: "note";
22
+ style: CSSProperties;
23
+ title: string;
24
+ }, HTMLElement> | null;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * FailoverNoticeRow — the daruma chat renderer for {@link FAILOVER_NOTICE_KIND}.
3
+ *
4
+ * A single quiet row inside the conversation flow telling the user that
5
+ * daruma caught a failed model request and switched the channel for the
6
+ * retry. Registered behind the host's keyed `conversation.chat.node` seat,
7
+ * so it renders exactly like an in-repo chat row (no DOM injection).
8
+ */
9
+ import { createElement as h } from 'react';
10
+ import { failoverNoticeCopy, parseFailoverNotice, } from "./failover-notice.js";
11
+ const rowStyle = {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: '6px',
15
+ minWidth: 0,
16
+ padding: '2px 4px',
17
+ fontSize: '12px',
18
+ lineHeight: '18px',
19
+ color: 'var(--dsw-alias-label-secondary)',
20
+ };
21
+ const dotStyle = {
22
+ width: '6px',
23
+ height: '6px',
24
+ borderRadius: '50%',
25
+ flex: 'none',
26
+ background: 'var(--dsw-static-deepseek-500)',
27
+ };
28
+ /** Give-up rows use the error tint instead of the brand dot. */
29
+ const giveUpDotStyle = {
30
+ ...dotStyle,
31
+ background: 'var(--dsw-alias-status-error, #e5484d)',
32
+ };
33
+ const brandStyle = {
34
+ flex: 'none',
35
+ fontWeight: 600,
36
+ color: 'var(--dsw-alias-label-primary)',
37
+ };
38
+ const lineStyle = {
39
+ overflow: 'hidden',
40
+ textOverflow: 'ellipsis',
41
+ whiteSpace: 'nowrap',
42
+ minWidth: 0,
43
+ };
44
+ /** Render one settled failover / give-up row, or nothing when unusable. */
45
+ export function FailoverNoticeRow({ node, t }) {
46
+ const data = parseFailoverNotice(node.data);
47
+ if (data === null)
48
+ return null;
49
+ const { line, detail } = failoverNoticeCopy(data, t);
50
+ const title = line === detail ? detail : `${line} · ${detail}`;
51
+ const isGiveUp = data.kind === 'give-up';
52
+ return h('div', {
53
+ 'data-daruma-notice': isGiveUp ? 'give-up' : 'failover',
54
+ role: 'note',
55
+ style: rowStyle,
56
+ title,
57
+ }, [
58
+ h('span', { 'aria-hidden': true, style: isGiveUp ? giveUpDotStyle : dotStyle }),
59
+ h('span', { style: brandStyle }, 'daruma'),
60
+ h('span', { style: lineStyle }, line),
61
+ ]);
62
+ }
63
+ //# sourceMappingURL=FailoverNoticeRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailoverNoticeRow.js","sourceRoot":"","sources":["../../src/client/FailoverNoticeRow.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,IAAI,CAAC,EAAsB,MAAM,OAAO,CAAA;AAC9D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GAEpB,MAAM,sBAAsB,CAAA;AAQ7B,MAAM,QAAQ,GAAkB;IAC9B,OAAO,EAAE,MAAM;IACf,UAAU,EAAE,QAAQ;IACpB,GAAG,EAAE,KAAK;IACV,QAAQ,EAAE,CAAC;IACX,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,KAAK,EAAE,kCAAkC;CAC1C,CAAA;AAED,MAAM,QAAQ,GAAkB;IAC9B,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,KAAK;IACb,YAAY,EAAE,KAAK;IACnB,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,gCAAgC;CAC7C,CAAA;AAED,gEAAgE;AAChE,MAAM,cAAc,GAAkB;IACpC,GAAG,QAAQ;IACX,UAAU,EAAE,wCAAwC;CACrD,CAAA;AAED,MAAM,UAAU,GAAkB;IAChC,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,GAAG;IACf,KAAK,EAAE,gCAAgC;CACxC,CAAA;AAED,MAAM,SAAS,GAAkB;IAC/B,QAAQ,EAAE,QAAQ;IAClB,YAAY,EAAE,UAAU;IACxB,UAAU,EAAE,QAAQ;IACpB,QAAQ,EAAE,CAAC;CACZ,CAAA;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC,EAA0B;IACnE,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAe,CAAC,CAAA;IACtD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC9B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,MAAM,EAAE,CAAA;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;IACxC,OAAO,CAAC,CACN,KAAK,EACL;QACE,oBAAoB,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU;QACvD,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,QAAQ;QACf,KAAK;KACN,EACD;QACE,CAAC,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC/E,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,QAAQ,CAAC;QAC1C,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,CAAC;KACtC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Pure failover-notice logic for the daruma web client.
3
+ *
4
+ * Bridges the server-appended `daruma/failover` session event into a visible
5
+ * row inside the DSH conversation window. Host chat rendering is
6
+ * definition-driven and out-of-repo event types are never rendered on their
7
+ * own (the host fallback only claims append-surface events), so the client
8
+ * registers its own conversation definition (kind {@link FAILOVER_NOTICE_KIND},
9
+ * target `chat`) plus a keyed renderer for that kind.
10
+ *
11
+ * This module is framework- and DOM-free on purpose: the event contract and
12
+ * the produced chat node are unit-testable without mounting a browser.
13
+ */
14
+ /** Session event type appended by the daruma server on every channel switch. */
15
+ export declare const FAILOVER_EVENT_TYPE = "daruma/failover";
16
+ /** Session event type appended when recovery gives up entirely. */
17
+ export declare const GIVE_UP_EVENT_TYPE = "daruma/give-up";
18
+ /** Chat node kind (conversation-definition kind + renderer key) used here. */
19
+ export declare const FAILOVER_NOTICE_KIND = "daruma-failover";
20
+ /** Display data carved out of one `daruma/failover` or `daruma/give-up` event. */
21
+ export interface FailoverNoticeData {
22
+ /** Which recovery decision this row reports. */
23
+ readonly kind: 'failover' | 'give-up';
24
+ /** Channel id the request failed on, e.g. `mt::glm-5.3`. */
25
+ readonly from: string;
26
+ /** Channel id recovery switched to (`give-up` rows leave this empty). */
27
+ readonly to: string;
28
+ /** Stable failure code / give-up reason, e.g. `RATE_LIMIT`. */
29
+ readonly reason: string;
30
+ /** Epoch ms of the decision. */
31
+ readonly at: number;
32
+ /** Turn containing the failed request. */
33
+ readonly turn: number;
34
+ /** Step containing the failed request attempt. */
35
+ readonly step: number;
36
+ /** Scope failover count AFTER this decision (1-based on first failover). */
37
+ readonly count: number;
38
+ /** Scope give-up budget the count counts against. */
39
+ readonly budget: number;
40
+ }
41
+ /** Structural view of a raw session event (host types stay server-side). */
42
+ export interface RawSessionEventLike {
43
+ readonly type: string;
44
+ readonly seq: number;
45
+ readonly time: number;
46
+ readonly data?: unknown;
47
+ }
48
+ /** One accepted match, as the host engine hands it to a Definition. */
49
+ export interface ConversationMatchLike {
50
+ readonly event: RawSessionEventLike;
51
+ readonly location?: unknown;
52
+ }
53
+ /**
54
+ * Engine-owned Context handed to Definition hooks — the structural subset
55
+ * this client reads (host types live server-side in `@deepseek-ai/*`).
56
+ */
57
+ export interface ConversationContextLike {
58
+ readonly key: string;
59
+ readonly kind: string;
60
+ readonly id: string;
61
+ readonly state?: unknown;
62
+ readonly start?: ConversationMatchLike;
63
+ }
64
+ /** The final chat node this Definition materializes (structural subset). */
65
+ export interface ChatNodeLike {
66
+ readonly key: string;
67
+ readonly kind: string;
68
+ readonly id: string;
69
+ readonly target: 'chat';
70
+ readonly anchorSeq: number;
71
+ readonly location: unknown;
72
+ readonly visibility: 'visible';
73
+ readonly data: FailoverNoticeData;
74
+ }
75
+ /** Minimal host conversation-definition surface this client registers. */
76
+ export interface ConversationDefinitionLike {
77
+ readonly kind: string;
78
+ readonly target: 'chat';
79
+ match(event: RawSessionEventLike): {
80
+ readonly id: string;
81
+ readonly role: 'start' | 'update';
82
+ } | null;
83
+ start(context: ConversationContextLike, match: ConversationMatchLike): FailoverNoticeData;
84
+ update(context: ConversationContextLike, match: ConversationMatchLike): FailoverNoticeData;
85
+ buildViewNode(context: ConversationContextLike): ChatNodeLike | null;
86
+ }
87
+ /**
88
+ * Defensively parse one `daruma/failover` (or `daruma/give-up`) payload into
89
+ * display data. Returns `null` when the payload is not a usable record, so a
90
+ * malformed event never renders (and is never claimed by this definition).
91
+ * A `give-up` row needs only `from`; a `failover` row needs `from` and `to`.
92
+ */
93
+ export declare function parseFailoverNotice(raw: unknown): FailoverNoticeData | null;
94
+ /** Replace `{key}` placeholders with matching values; unknown keys stay. */
95
+ export declare function fillTemplate(template: string, values: Readonly<Record<string, string>>): string;
96
+ /** Human copy for one failover row, built from the bound daruma dictionary. */
97
+ export interface FailoverNoticeCopy {
98
+ /** The one-liner rendered inside the conversation flow. */
99
+ readonly line: string;
100
+ /** Longer context shown on hover (budget usage, turn/step). */
101
+ readonly detail: string;
102
+ }
103
+ /** Compose the row copy through the daruma locale dictionary. */
104
+ export declare function failoverNoticeCopy(data: FailoverNoticeData, t: (key: string) => string): FailoverNoticeCopy;
105
+ /**
106
+ * The conversation Definition claiming `daruma/failover` and `daruma/give-up`
107
+ * events for the chat target. One decision = one context (id = its event seq)
108
+ * = one settled row, anchored at the event's own log position so it lands
109
+ * inline in the flow.
110
+ */
111
+ export declare function createFailoverNoticeDefinition(): ConversationDefinitionLike;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Pure failover-notice logic for the daruma web client.
3
+ *
4
+ * Bridges the server-appended `daruma/failover` session event into a visible
5
+ * row inside the DSH conversation window. Host chat rendering is
6
+ * definition-driven and out-of-repo event types are never rendered on their
7
+ * own (the host fallback only claims append-surface events), so the client
8
+ * registers its own conversation definition (kind {@link FAILOVER_NOTICE_KIND},
9
+ * target `chat`) plus a keyed renderer for that kind.
10
+ *
11
+ * This module is framework- and DOM-free on purpose: the event contract and
12
+ * the produced chat node are unit-testable without mounting a browser.
13
+ */
14
+ /** Session event type appended by the daruma server on every channel switch. */
15
+ export const FAILOVER_EVENT_TYPE = 'daruma/failover';
16
+ /** Session event type appended when recovery gives up entirely. */
17
+ export const GIVE_UP_EVENT_TYPE = 'daruma/give-up';
18
+ /** Chat node kind (conversation-definition kind + renderer key) used here. */
19
+ export const FAILOVER_NOTICE_KIND = 'daruma-failover';
20
+ /** Display length caps mirroring the server-side event assembly. */
21
+ const CHANNEL_LIMIT = 80;
22
+ const REASON_LIMIT = 40;
23
+ function isRecord(value) {
24
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
25
+ }
26
+ /** Trim a payload string, clipping over-long values with an ellipsis. */
27
+ function clip(value, limit) {
28
+ if (typeof value !== 'string')
29
+ return undefined;
30
+ const text = value.trim();
31
+ if (text.length === 0)
32
+ return undefined;
33
+ return text.length <= limit ? text : `${text.slice(0, limit)}…`;
34
+ }
35
+ function finiteNumber(value) {
36
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
37
+ }
38
+ /**
39
+ * Defensively parse one `daruma/failover` (or `daruma/give-up`) payload into
40
+ * display data. Returns `null` when the payload is not a usable record, so a
41
+ * malformed event never renders (and is never claimed by this definition).
42
+ * A `give-up` row needs only `from`; a `failover` row needs `from` and `to`.
43
+ */
44
+ export function parseFailoverNotice(raw) {
45
+ if (!isRecord(raw))
46
+ return null;
47
+ const kind = raw.kind === 'give-up' ? 'give-up' : 'failover';
48
+ const from = clip(raw.from, CHANNEL_LIMIT);
49
+ const to = clip(raw.to, CHANNEL_LIMIT);
50
+ if (from === undefined)
51
+ return null;
52
+ if (kind === 'failover' && to === undefined)
53
+ return null;
54
+ const reason = clip(raw.reason, REASON_LIMIT) ?? 'FAILURE';
55
+ const at = finiteNumber(raw.at);
56
+ return {
57
+ kind,
58
+ from,
59
+ to: to ?? '',
60
+ reason,
61
+ at: at === 0 ? Date.now() : at,
62
+ turn: finiteNumber(raw.turn),
63
+ step: finiteNumber(raw.step),
64
+ count: finiteNumber(raw.failoverCount),
65
+ budget: finiteNumber(raw.giveUpBudget),
66
+ };
67
+ }
68
+ /** Replace `{key}` placeholders with matching values; unknown keys stay. */
69
+ export function fillTemplate(template, values) {
70
+ return template.replace(/\{(\w+)\}/g, (match, name) => name in values ? values[name] ?? '' : match);
71
+ }
72
+ /** Compose the row copy through the daruma locale dictionary. */
73
+ export function failoverNoticeCopy(data, t) {
74
+ if (data.kind === 'give-up') {
75
+ const line = fillTemplate(t('failoverGiveUpLine'), {
76
+ from: data.from,
77
+ reason: data.reason,
78
+ });
79
+ const detail = fillTemplate(t('failoverGiveUpDetail'), {
80
+ count: String(data.count),
81
+ budget: String(data.budget),
82
+ turn: String(data.turn),
83
+ step: String(data.step),
84
+ });
85
+ return { line, detail };
86
+ }
87
+ const line = fillTemplate(t('failoverNoticeLine'), {
88
+ from: data.from,
89
+ to: data.to,
90
+ reason: data.reason,
91
+ });
92
+ const detail = fillTemplate(t('failoverNoticeDetail'), {
93
+ count: String(data.count),
94
+ budget: String(data.budget),
95
+ turn: String(data.turn),
96
+ step: String(data.step),
97
+ });
98
+ return { line, detail };
99
+ }
100
+ /**
101
+ * The conversation Definition claiming `daruma/failover` and `daruma/give-up`
102
+ * events for the chat target. One decision = one context (id = its event seq)
103
+ * = one settled row, anchored at the event's own log position so it lands
104
+ * inline in the flow.
105
+ */
106
+ export function createFailoverNoticeDefinition() {
107
+ const start = (_context, match) => {
108
+ const parsed = parseFailoverNotice(match.event.data);
109
+ if (parsed === null) {
110
+ throw new Error(`daruma failover notice: unparseable ${FAILOVER_EVENT_TYPE} event at seq ${match.event.seq}`);
111
+ }
112
+ return parsed;
113
+ };
114
+ return {
115
+ kind: FAILOVER_NOTICE_KIND,
116
+ target: 'chat',
117
+ match: (event) => {
118
+ if (event.type !== FAILOVER_EVENT_TYPE && event.type !== GIVE_UP_EVENT_TYPE)
119
+ return null;
120
+ if (parseFailoverNotice(event.data) === null)
121
+ return null;
122
+ return { id: String(event.seq), role: 'start' };
123
+ },
124
+ start,
125
+ // Every claimed event is a standalone start; keep update total for safety.
126
+ update: (context, match) => start(context, match),
127
+ buildViewNode: (context) => {
128
+ const match = context.start;
129
+ if (match === undefined)
130
+ return null;
131
+ const data = parseFailoverNotice(match.event.data);
132
+ if (data === null)
133
+ return null;
134
+ return {
135
+ key: context.key,
136
+ kind: context.kind,
137
+ id: context.id,
138
+ target: 'chat',
139
+ anchorSeq: match.event.seq,
140
+ location: match.location ?? { kind: 'unresolved' },
141
+ visibility: 'visible',
142
+ data,
143
+ };
144
+ },
145
+ };
146
+ }
147
+ //# sourceMappingURL=failover-notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failover-notice.js","sourceRoot":"","sources":["../../src/client/failover-notice.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Time-aware channel-health classification for the status dock.
3
3
  *
4
- * The host exposes no request-success event, so a channel whose cooldown has
5
- * expired keeps its `COOLDOWN` state field forever it becomes routable again
6
- * (`canRouteNow`) without ever flipping back to `HEALTHY`. The dock therefore
7
- * must judge by time, not by the raw state field: only a channel that is
8
- * *still* cooling (cooldown not yet expired) counts as an error.
4
+ * The adapter now records successes (inferred from `agent/pre-step`), which
5
+ * resets a channel to HEALTHY on its first successful request. But a channel
6
+ * that never routes again (removed from the chain, one-shot backup) keeps its
7
+ * last persisted state forever including a stale COOLDOWN whose deadline
8
+ * has long passed. The dock therefore still judges by time, not by the raw
9
+ * state field: only a channel that is *still* cooling (cooldown not yet
10
+ * expired) counts as an error.
9
11
  */
10
12
  import type { ChannelHealthView } from './api.ts';
11
13
  export type Dot = 'done' | 'warning' | 'ongoing' | 'error';
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Time-aware channel-health classification for the status dock.
3
3
  *
4
- * The host exposes no request-success event, so a channel whose cooldown has
5
- * expired keeps its `COOLDOWN` state field forever it becomes routable again
6
- * (`canRouteNow`) without ever flipping back to `HEALTHY`. The dock therefore
7
- * must judge by time, not by the raw state field: only a channel that is
8
- * *still* cooling (cooldown not yet expired) counts as an error.
4
+ * The adapter now records successes (inferred from `agent/pre-step`), which
5
+ * resets a channel to HEALTHY on its first successful request. But a channel
6
+ * that never routes again (removed from the chain, one-shot backup) keeps its
7
+ * last persisted state forever including a stale COOLDOWN whose deadline
8
+ * has long passed. The dock therefore still judges by time, not by the raw
9
+ * state field: only a channel that is *still* cooling (cooldown not yet
10
+ * expired) counts as an error.
9
11
  */
10
12
  /**
11
13
  * Overall dock state:
@@ -1 +1 @@
1
- {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/client/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA6C,EAC7C,KAAa;IAEb,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACvC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,IAAI,CAAC,CAAC,eAAe,GAAG,KAAK,CAAC;QAAE,OAAO,OAAO,CAAA;IAC7F,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IAC/D,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/client/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA6C,EAC7C,KAAa;IAEb,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACvC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,IAAI,CAAC,CAAC,eAAe,GAAG,KAAK,CAAC;QAAE,OAAO,OAAO,CAAA;IAC7F,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IAC/D,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -8,7 +8,44 @@ import { createElement as h } from 'react';
8
8
  import { en, zh } from "./locales.js";
9
9
  import { createApi } from "./api.js";
10
10
  import { StatusDock } from "./StatusDock.js";
11
+ import { createFailoverNoticeDefinition, FAILOVER_NOTICE_KIND, } from "./failover-notice.js";
12
+ import { FailoverNoticeRow } from "./FailoverNoticeRow.js";
11
13
  const NS = 'daruma';
14
+ /**
15
+ * Register the live failover notice: one conversation Definition claiming
16
+ * `daruma/failover` events for the chat target, plus the keyed renderer for
17
+ * its node kind. Absent or failing registrations degrade to nothing visible
18
+ * (warn only) — the panel keeps working either way.
19
+ */
20
+ function registerFailoverChatNotice(ctx) {
21
+ const registry = ctx.get('conversationEvents');
22
+ if (registry === undefined) {
23
+ console.warn('[dsh-daruma] conversation event registry unavailable — failover chat rows disabled');
24
+ }
25
+ else {
26
+ try {
27
+ ctx.effect(() => registry.register(createFailoverNoticeDefinition()), 'dsh-daruma: failover notice definition');
28
+ }
29
+ catch (error) {
30
+ console.warn('[dsh-daruma] failover notice definition registration failed', error);
31
+ }
32
+ }
33
+ ctx.slots.inject('conversation.chat.node', () => {
34
+ try {
35
+ // The keyed seat instantiates entries with props (node, t, …); the
36
+ // structural slot type is narrower than the host's real registry.
37
+ return ctx.slots.register({
38
+ name: 'conversation.chat.node',
39
+ key: FAILOVER_NOTICE_KIND,
40
+ locale: NS,
41
+ }, FailoverNoticeRow);
42
+ }
43
+ catch (error) {
44
+ console.warn('[dsh-daruma] failover notice renderer registration failed', error);
45
+ return () => { };
46
+ }
47
+ });
48
+ }
12
49
  export const name = 'dsh-daruma';
13
50
  export const inject = ['slots', 'locale'];
14
51
  export function apply(ctx) {
@@ -39,5 +76,6 @@ export function apply(ctx) {
39
76
  order: 0,
40
77
  inject: () => ({ api, t }),
41
78
  }, () => h(StatusDock, { api, t })));
79
+ registerFailoverChatNotice(ctx);
42
80
  }
43
81
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,IAAI,CAAC,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,SAAS,EAA4B,MAAM,UAAU,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAkB,CAAA;AAE7C,MAAM,EAAE,GAAG,QAAQ,CAAA;AAyBnB,MAAM,CAAC,MAAM,IAAI,GAAG,YAAY,CAAA;AAChC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAU,CAAA;AAElD,MAAM,UAAU,KAAK,CAAC,GAAkB;IACtC,uEAAuE;IACvE,wEAAwE;IACxE,sEAAsE;IACtE,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,oBAAoB,CAAA;QAClC,IAAI,QAAQ,CAAC,aAAa,CAAC,sBAAsB,KAAK,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACrE,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;YAC7C,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAA;YAC5B,KAAK,CAAC,WAAW,GAAG,qEAAqE,CAAA;YACzF,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAClC,CAAC;IACH,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,0BAA0B,CAAC,CAAA;IACjF,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC7B,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,CAA+B,CAAA;IACtE,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAA;QAC3E,OAAM;IACR,CAAC;IACD,MAAM,GAAG,GAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CACrC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAuB,CAAA;IACnF,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;IAE1B,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;QACpE,IAAI,EAAE,0BAA0B;QAChC,EAAE,EAAE,eAAe;QACnB,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAC3B,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;AACtC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,10 @@ export declare const zh: {
24
24
  currentBackupMark: string;
25
25
  noCandidates: string;
26
26
  empty: string;
27
+ failoverNoticeLine: string;
28
+ failoverNoticeDetail: string;
29
+ failoverGiveUpLine: string;
30
+ failoverGiveUpDetail: string;
27
31
  };
28
32
  export declare const en: {
29
33
  status: string;
@@ -50,4 +54,8 @@ export declare const en: {
50
54
  currentBackupMark: string;
51
55
  noCandidates: string;
52
56
  empty: string;
57
+ failoverNoticeLine: string;
58
+ failoverNoticeDetail: string;
59
+ failoverGiveUpLine: string;
60
+ failoverGiveUpDetail: string;
53
61
  };
@@ -24,6 +24,10 @@ export const zh = {
24
24
  currentBackupMark: '备用',
25
25
  noCandidates: '未加载到候选模型',
26
26
  empty: '暂无渠道数据',
27
+ failoverNoticeLine: '{from} 请求失败({reason})→ 本次尝试 {to}',
28
+ failoverNoticeDetail: '第 {count}/{budget} 次失败切换 · turn {turn} step {step}',
29
+ failoverGiveUpLine: '{from} 请求失败({reason})→ 已用尽恢复手段,停止重试',
30
+ failoverGiveUpDetail: '失败切换 {count}/{budget} · turn {turn} step {step}',
27
31
  };
28
32
  export const en = {
29
33
  status: 'Channel status',
@@ -50,5 +54,9 @@ export const en = {
50
54
  currentBackupMark: 'backup',
51
55
  noCandidates: 'No candidates loaded',
52
56
  empty: 'No channel data yet',
57
+ failoverNoticeLine: '{from} failed ({reason}) → trying {to}',
58
+ failoverNoticeDetail: 'failover {count}/{budget} · turn {turn} step {step}',
59
+ failoverGiveUpLine: '{from} failed ({reason}) → recovery budget exhausted, giving up',
60
+ failoverGiveUpDetail: 'failovers {count}/{budget} · turn {turn} step {step}',
53
61
  };
54
62
  //# sourceMappingURL=locales.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"locales.js","sourceRoot":"","sources":["../../src/client/locales.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,IAAI;IACb,MAAM,EAAE,IAAI;IACZ,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,MAAM;IACpB,gBAAgB,EAAE,aAAa;IAC/B,aAAa,EAAE,KAAK;IACpB,YAAY,EAAE,MAAM;IACpB,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,MAAM;IACnB,QAAQ,EAAE,KAAK;IACf,mBAAmB,EAAE,eAAe;IACpC,cAAc,EAAE,MAAM;IACtB,OAAO,EAAE,MAAM;IACf,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE,MAAM;IACrB,iBAAiB,EAAE,IAAI;IACvB,YAAY,EAAE,UAAU;IACxB,KAAK,EAAE,QAAQ;CAChB,CAAA;AAED,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,MAAM;IAClB,YAAY,EAAE,gBAAgB;IAC9B,gBAAgB,EAAE,+CAA+C;IACjE,aAAa,EAAE,WAAW;IAC1B,YAAY,EAAE,eAAe;IAC7B,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,SAAS;IACnB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,cAAc;IAC3B,QAAQ,EAAE,UAAU;IACpB,mBAAmB,EAAE,mBAAmB;IACxC,cAAc,EAAE,iBAAiB;IACjC,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,kBAAkB;IAC9B,SAAS,EAAE,eAAe;IAC1B,aAAa,EAAE,gBAAgB;IAC/B,iBAAiB,EAAE,QAAQ;IAC3B,YAAY,EAAE,sBAAsB;IACpC,KAAK,EAAE,qBAAqB;CAC7B,CAAA"}
1
+ {"version":3,"file":"locales.js","sourceRoot":"","sources":["../../src/client/locales.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,IAAI;IACb,MAAM,EAAE,IAAI;IACZ,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,MAAM;IACpB,gBAAgB,EAAE,aAAa;IAC/B,aAAa,EAAE,KAAK;IACpB,YAAY,EAAE,MAAM;IACpB,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,MAAM;IACnB,QAAQ,EAAE,KAAK;IACf,mBAAmB,EAAE,eAAe;IACpC,cAAc,EAAE,MAAM;IACtB,OAAO,EAAE,MAAM;IACf,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE,MAAM;IACrB,iBAAiB,EAAE,IAAI;IACvB,YAAY,EAAE,UAAU;IACxB,KAAK,EAAE,QAAQ;IACf,kBAAkB,EAAE,kCAAkC;IACtD,oBAAoB,EAAE,oDAAoD;IAC1E,kBAAkB,EAAE,qCAAqC;IACzD,oBAAoB,EAAE,iDAAiD;CACxE,CAAA;AAED,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,MAAM;IAClB,YAAY,EAAE,gBAAgB;IAC9B,gBAAgB,EAAE,+CAA+C;IACjE,aAAa,EAAE,WAAW;IAC1B,YAAY,EAAE,eAAe;IAC7B,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,SAAS;IACnB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,cAAc;IAC3B,QAAQ,EAAE,UAAU;IACpB,mBAAmB,EAAE,mBAAmB;IACxC,cAAc,EAAE,iBAAiB;IACjC,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,kBAAkB;IAC9B,SAAS,EAAE,eAAe;IAC1B,aAAa,EAAE,gBAAgB;IAC/B,iBAAiB,EAAE,QAAQ;IAC3B,YAAY,EAAE,sBAAsB;IACpC,KAAK,EAAE,qBAAqB;IAC5B,kBAAkB,EAAE,wCAAwC;IAC5D,oBAAoB,EAAE,qDAAqD;IAC3E,kBAAkB,EAAE,iEAAiE;IACrF,oBAAoB,EAAE,sDAAsD;CAC7E,CAAA"}