@khorsheed/dsh-local-agent 0.1.0-rc.6

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 (99) hide show
  1. package/README.en.md +181 -0
  2. package/README.i18n.yaml +2 -0
  3. package/README.md +181 -0
  4. package/cordis.patch.yml +21 -0
  5. package/lib/client.js +8832 -0
  6. package/lib/index.js +5281 -0
  7. package/lib/invariant.js +85 -0
  8. package/lib/member-bridge.js +298 -0
  9. package/lib/typert.host.d.ts +3 -0
  10. package/lib/typert.host.js +3137 -0
  11. package/lib/typert.remote-client.d.ts +67 -0
  12. package/lib/typert.remote-client.js +1971 -0
  13. package/lib/types/cli-version.d.ts +82 -0
  14. package/lib/types/cli-version.js +165 -0
  15. package/lib/types/client/AuthStatusDot.d.ts +17 -0
  16. package/lib/types/client/AuthStatusDot.js +9 -0
  17. package/lib/types/client/HarnessModelPicker.d.ts +17 -0
  18. package/lib/types/client/HarnessModelPicker.js +48 -0
  19. package/lib/types/client/LocalAgentRecordsAction.d.ts +41 -0
  20. package/lib/types/client/LocalAgentRecordsAction.js +74 -0
  21. package/lib/types/client/MemberComposer.d.ts +79 -0
  22. package/lib/types/client/MemberComposer.js +308 -0
  23. package/lib/types/client/MemberConfiguration.d.ts +9 -0
  24. package/lib/types/client/MemberConfiguration.js +46 -0
  25. package/lib/types/client/MemberInboxView.d.ts +12 -0
  26. package/lib/types/client/MemberInboxView.js +38 -0
  27. package/lib/types/client/MemberLiveNode.d.ts +14 -0
  28. package/lib/types/client/MemberLiveNode.js +74 -0
  29. package/lib/types/client/MemberPaintDiagnostics.d.ts +7 -0
  30. package/lib/types/client/MemberPaintDiagnostics.js +20 -0
  31. package/lib/types/client/ModelConfigurationFields.d.ts +15 -0
  32. package/lib/types/client/ModelConfigurationFields.js +23 -0
  33. package/lib/types/client/ProviderAuthBlock.d.ts +59 -0
  34. package/lib/types/client/ProviderAuthBlock.js +164 -0
  35. package/lib/types/client/auth-status.d.ts +28 -0
  36. package/lib/types/client/auth-status.js +56 -0
  37. package/lib/types/client/icons.d.ts +22 -0
  38. package/lib/types/client/icons.js +6 -0
  39. package/lib/types/client/index.d.ts +62 -0
  40. package/lib/types/client/index.js +135 -0
  41. package/lib/types/client/live-node.d.ts +14 -0
  42. package/lib/types/client/live-node.js +34 -0
  43. package/lib/types/client/live-output.d.ts +34 -0
  44. package/lib/types/client/live-output.js +94 -0
  45. package/lib/types/client/live-paint.d.ts +40 -0
  46. package/lib/types/client/live-paint.js +74 -0
  47. package/lib/types/client/locales.d.ts +120 -0
  48. package/lib/types/client/locales.js +227 -0
  49. package/lib/types/client/member-configuration.d.ts +41 -0
  50. package/lib/types/client/member-configuration.js +114 -0
  51. package/lib/types/client/member-dock.d.ts +94 -0
  52. package/lib/types/client/member-dock.js +102 -0
  53. package/lib/types/client/member-feed.d.ts +24 -0
  54. package/lib/types/client/member-feed.js +134 -0
  55. package/lib/types/client/request-id.d.ts +3 -0
  56. package/lib/types/client/request-id.js +6 -0
  57. package/lib/types/client/settings-scope.d.ts +74 -0
  58. package/lib/types/client/settings-scope.js +98 -0
  59. package/lib/types/client/use-configuration-menu.d.ts +10 -0
  60. package/lib/types/client/use-configuration-menu.js +61 -0
  61. package/lib/types/container.d.ts +69 -0
  62. package/lib/types/container.js +129 -0
  63. package/lib/types/env.d.ts +17 -0
  64. package/lib/types/env.js +89 -0
  65. package/lib/types/gateway.d.ts +162 -0
  66. package/lib/types/gateway.js +470 -0
  67. package/lib/types/index.d.ts +1463 -0
  68. package/lib/types/index.js +2704 -0
  69. package/lib/types/invariant.d.ts +16 -0
  70. package/lib/types/invariant.js +105 -0
  71. package/lib/types/live-flush.d.ts +21 -0
  72. package/lib/types/live-flush.js +59 -0
  73. package/lib/types/live-stream.d.ts +41 -0
  74. package/lib/types/live-stream.js +178 -0
  75. package/lib/types/member-bridge.d.ts +50 -0
  76. package/lib/types/member-bridge.js +216 -0
  77. package/lib/types/member-channel.d.ts +86 -0
  78. package/lib/types/member-channel.js +232 -0
  79. package/lib/types/member-control-storage.d.ts +10 -0
  80. package/lib/types/member-control-storage.js +100 -0
  81. package/lib/types/member-control.d.ts +62 -0
  82. package/lib/types/member-control.js +359 -0
  83. package/lib/types/member-controls.d.ts +23 -0
  84. package/lib/types/member-controls.js +143 -0
  85. package/lib/types/member-feed.d.ts +4 -0
  86. package/lib/types/member-feed.js +45 -0
  87. package/lib/types/member-inbox.d.ts +30 -0
  88. package/lib/types/member-inbox.js +225 -0
  89. package/lib/types/model-directory.d.ts +43 -0
  90. package/lib/types/model-directory.js +192 -0
  91. package/lib/types/prepared-members.d.ts +15 -0
  92. package/lib/types/prepared-members.js +61 -0
  93. package/lib/types/settings-face.d.ts +61 -0
  94. package/lib/types/settings-face.js +61 -0
  95. package/lib/types/types.d.ts +959 -0
  96. package/lib/types/types.js +24 -0
  97. package/locale/en.json +6 -0
  98. package/locale/zh.json +6 -0
  99. package/package.json +112 -0
@@ -0,0 +1,59 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
3
+ import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client';
4
+ import type { LocalAgentStatus } from '@khorsheed/dsh-local-agent/types';
5
+ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
6
+ import { NS } from './locales.ts';
7
+ import type { LocalAgentHarnessView } from './LocalAgentRecordsAction.tsx';
8
+ /** Known harnesses the family plans to support; unregistered ones render as pending. */
9
+ export declare const KNOWN_HARNESSES: readonly LocalAgentHarnessView[];
10
+ /** How often a pending login's status is re-probed after the prompt surfaces. */
11
+ export declare const LOGIN_POLL_MS = 4000;
12
+ /** How long a pending login keeps being polled before the section gives up. */
13
+ export declare const LOGIN_POLL_LIMIT_MS: number;
14
+ /** The first http(s) URL in a login prompt, if any. */
15
+ export declare function parseLoginUrl(text: string): string | undefined;
16
+ /**
17
+ * Injected business face of the shared auth block — the subset of the
18
+ * settings section's injected face one harness row consumes.
19
+ */
20
+ export interface ProviderAuthInjected {
21
+ /** One harness's auth status. */
22
+ status: (name: string) => Promise<LocalAgentStatus | undefined>;
23
+ /**
24
+ * Run one user-initiated slash-command line (login/logout) against the
25
+ * session's agent and return the command result text, or undefined when
26
+ * unavailable.
27
+ * @param sessionId - the target session.
28
+ * @param line - the full command line.
29
+ */
30
+ runCommand: (sessionId: SessionId, line: string) => Promise<string | undefined>;
31
+ }
32
+ /** Full props for the shared per-harness auth block. */
33
+ export interface ProviderAuthBlockProps extends ProviderAuthInjected, PropsLocale<typeof NS> {
34
+ /** The harness this block manages. */
35
+ harness: LocalAgentHarnessView;
36
+ /** Global sessions hook (the slot runtime share, delivered to every slot). */
37
+ useSessions: <T>(select: (state: SessionListState) => T) => T;
38
+ /**
39
+ * Extra actions rendered at the end of the action row — the settings
40
+ * section passes its `local-agent.settings.row-action` seat here.
41
+ */
42
+ actions?: ReactNode;
43
+ }
44
+ /**
45
+ * One harness's auth interactions, shared between the per-provider settings
46
+ * cards (the alpha.2 `plugins.bundle.config` pages and the 0.1.5
47
+ * `settings.plugin.item` tab): auth status through
48
+ * the read-only Remote channel and a web-login action that runs
49
+ * `/<harness> login`, surfaces the device-code prompt, and offers a link to
50
+ * the authorization page. The login command returns at prompt time while the
51
+ * CLI polls in the background, so a pending login's status keeps being
52
+ * re-probed until credentials land or the poll window expires. Status polls
53
+ * ride the Remote channel and emit no session events; only the user-initiated
54
+ * login/logout commands leave command nodes in the log.
55
+ * @param props - the harness, the injected query/command faces, and copy.
56
+ * @returns the block content (head row, prompts, and the login toast).
57
+ */
58
+ export declare function ProviderAuthBlock({ harness, useSessions, status, runCommand, actions, t }: ProviderAuthBlockProps): import("react").JSX.Element;
59
+ //# sourceMappingURL=ProviderAuthBlock.d.ts.map
@@ -0,0 +1,164 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { Toast } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { IconCheckOutlineMedium } from './icons.js';
5
+ import { publishAuthStatus } from './auth-status.js';
6
+ import css from './ProviderAuthBlock.module.css';
7
+ /** Known harnesses the family plans to support; unregistered ones render as pending. */
8
+ export const KNOWN_HARNESSES = [
9
+ { id: 'kimi', label: 'Kimi Code' },
10
+ { id: 'codex', label: 'Codex' },
11
+ { id: 'claude-code', label: 'Claude Code' },
12
+ ];
13
+ /** How often a pending login's status is re-probed after the prompt surfaces. */
14
+ export const LOGIN_POLL_MS = 4_000;
15
+ /** How long a pending login keeps being polled before the section gives up. */
16
+ export const LOGIN_POLL_LIMIT_MS = 5 * 60_000;
17
+ /** The first http(s) URL in a login prompt, if any. */
18
+ export function parseLoginUrl(text) {
19
+ return /https?:\/\/\S+/.exec(text)?.[0];
20
+ }
21
+ /** Resolve a harness id to its display label for toast text. */
22
+ function harnessLabel(id) {
23
+ return KNOWN_HARNESSES.find(harness => harness.id === id)?.label ?? id;
24
+ }
25
+ /**
26
+ * The main-view session: host 0.1.6-alpha.2 dropped SessionListState.current
27
+ * for the retainedBy.mainView count on each summary (the label is ui-session's
28
+ * declaration merge, read here as a plain duck-typed count so this package
29
+ * needs no ui-session edge), with the legacy field as the older-host fallback.
30
+ */
31
+ const mainSessionId = (list) => Object.values(list.byId ?? {}).find(session => (session.retainedBy?.mainView ?? 0) > 0)?.id
32
+ ?? list.current;
33
+ /**
34
+ * One harness's auth interactions, shared between the per-provider settings
35
+ * cards (the alpha.2 `plugins.bundle.config` pages and the 0.1.5
36
+ * `settings.plugin.item` tab): auth status through
37
+ * the read-only Remote channel and a web-login action that runs
38
+ * `/<harness> login`, surfaces the device-code prompt, and offers a link to
39
+ * the authorization page. The login command returns at prompt time while the
40
+ * CLI polls in the background, so a pending login's status keeps being
41
+ * re-probed until credentials land or the poll window expires. Status polls
42
+ * ride the Remote channel and emit no session events; only the user-initiated
43
+ * login/logout commands leave command nodes in the log.
44
+ * @param props - the harness, the injected query/command faces, and copy.
45
+ * @returns the block content (head row, prompts, and the login toast).
46
+ */
47
+ export function ProviderAuthBlock({ harness, useSessions, status, runCommand, actions, t }) {
48
+ const sessionId = useSessions((state) => mainSessionId(state));
49
+ const [view, setView] = useState({ status: 'checking' });
50
+ /** The login start time while the device-code login is still pending. */
51
+ const [pendingSince, setPendingSince] = useState(undefined);
52
+ /** A login-completion toast awaiting mount; seq forces a re-show for repeats. */
53
+ const [loginToast, setLoginToast] = useState(null);
54
+ const toastSeq = useRef(0);
55
+ /** OAuth code draft for a pty login awaiting a paste. */
56
+ const [codeDraft, setCodeDraft] = useState('');
57
+ const refresh = () => {
58
+ const id = harness.id;
59
+ setView(prev => ({ ...prev, status: 'checking' }));
60
+ void status(id).then((probe) => {
61
+ const statusKind = probe === undefined ? 'unavailable'
62
+ : probe.authenticated ? 'authenticated' : 'anonymous';
63
+ // Card headers read the same probe through the status bus (the dot
64
+ // flips the moment a login/logout lands, in every mounted card).
65
+ publishAuthStatus(id, statusKind);
66
+ const capabilities = probe === undefined
67
+ ? {}
68
+ : {
69
+ loginable: probe.loginable ?? true,
70
+ logoutable: probe.logoutable ?? true,
71
+ ...probe.loginAwaitingCode === true ? { loginAwaitingCode: true } : {},
72
+ };
73
+ setView(prev =>
74
+ // A completed login drops the stale device prompt.
75
+ statusKind === 'authenticated'
76
+ ? { status: statusKind, ...capabilities }
77
+ : { ...prev, status: statusKind, ...capabilities });
78
+ if (statusKind === 'authenticated') {
79
+ setPendingSince((prev) => {
80
+ // A pending→authenticated transition (a start time was tracked) is
81
+ // the authorization-complete moment; surface it as a success toast.
82
+ if (prev !== undefined) {
83
+ toastSeq.current += 1;
84
+ setLoginToast({ seq: toastSeq.current });
85
+ }
86
+ return undefined;
87
+ });
88
+ }
89
+ }).catch(() => {
90
+ publishAuthStatus(id, 'unavailable');
91
+ setView(prev => ({ ...prev, status: 'unavailable' }));
92
+ });
93
+ };
94
+ useEffect(() => {
95
+ refresh();
96
+ }, [harness.id, status, sessionId]);
97
+ useEffect(() => {
98
+ if (sessionId === undefined || pendingSince === undefined)
99
+ return;
100
+ const timer = window.setInterval(() => {
101
+ if (Date.now() - pendingSince > LOGIN_POLL_LIMIT_MS) {
102
+ setPendingSince(undefined);
103
+ return;
104
+ }
105
+ refresh();
106
+ }, LOGIN_POLL_MS);
107
+ return () => { window.clearInterval(timer); };
108
+ }, [pendingSince, status, sessionId, harness.id]);
109
+ const startLogin = () => {
110
+ if (sessionId === undefined)
111
+ return;
112
+ void runCommand(sessionId, `/${harness.id} login`).then((text) => {
113
+ const text2 = text ?? '';
114
+ const loginUrl = parseLoginUrl(text2);
115
+ setView({
116
+ status: 'checking',
117
+ loginText: text2,
118
+ ...loginUrl !== undefined && { loginUrl },
119
+ });
120
+ setPendingSince(Date.now());
121
+ }).catch(() => {
122
+ setView({ status: 'unavailable' });
123
+ });
124
+ };
125
+ const signOut = () => {
126
+ if (sessionId === undefined)
127
+ return;
128
+ void runCommand(sessionId, `/${harness.id} logout`).then((text) => {
129
+ // Show the sign-out reply where the device prompt renders (a missing
130
+ // logout path is visible as its error text), then re-probe so the row
131
+ // flips to not authenticated.
132
+ setView({
133
+ status: 'checking',
134
+ loginText: text !== undefined && text.trim() !== '' ? text : t('settings.loggedOut'),
135
+ });
136
+ refresh();
137
+ }).catch(() => {
138
+ setView({ status: 'unavailable' });
139
+ });
140
+ };
141
+ const statusKind = view.status;
142
+ const authenticated = statusKind === 'authenticated';
143
+ const loginPending = pendingSince !== undefined;
144
+ // A harness without a login/logout flow (dsh authenticates through the host
145
+ // credentials) must not offer the actions its command family would answer
146
+ // with an error.
147
+ const loginable = view.loginable ?? true;
148
+ const logoutable = view.logoutable ?? true;
149
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.rowHead, children: [_jsxs("span", { className: css.rowIdentity, children: [_jsx("span", { className: css.rowName, children: harness.label }), _jsx("span", { className: `${css.credentialDot} ${authenticated ? css.dotOn : css.dotOff}`, role: "img", "aria-label": t(authenticated ? 'settings.authenticated' : 'settings.notAuthenticated') }), _jsx("span", { className: css.rowTag, children: harness.id })] }), _jsxs("span", { className: css.status, children: [statusKind === 'checking' && t('loading'), statusKind === 'authenticated' && t('settings.authenticated'), statusKind === 'anonymous' && t('settings.notAuthenticated'), statusKind === 'unavailable' && t('error')] }), _jsxs("span", { className: css.rowActions, children: [authenticated && logoutable && (_jsx("button", { type: "button", className: css.logoutButton, onClick: () => { signOut(); }, children: t('settings.logout') })), loginable && (_jsx("button", { type: "button", className: css.loginButton, disabled: loginPending || sessionId === undefined, onClick: () => { startLogin(); }, children: authenticated ? t('settings.reauthorize') : t('settings.login') })), actions] })] }), view.loginText !== undefined && (_jsxs("div", { className: css.loginPrompt, children: [_jsx("span", { className: css.promptText, children: view.loginText }), view.loginUrl !== undefined && (_jsx("a", { className: css.openPage, href: view.loginUrl, target: "_blank", rel: "noreferrer", children: t('settings.openPage') }))] })), view.loginAwaitingCode === true && (_jsxs("div", { className: css.loginPrompt, children: [_jsx("input", { className: css.codeInput, value: codeDraft, placeholder: t('settings.pasteCode'), onChange: (event) => {
150
+ setCodeDraft(event.currentTarget.value);
151
+ } }), _jsx("button", { type: "button", className: css.loginButton, disabled: codeDraft.trim() === '', onClick: () => {
152
+ const code = codeDraft.trim();
153
+ if (code === '' || sessionId === undefined)
154
+ return;
155
+ void runCommand(sessionId, `/${harness.id} code ${code}`).then((text) => {
156
+ setView((prev) => {
157
+ // Drop the awaiting flag; the command reply becomes the prompt text.
158
+ const { loginAwaitingCode: _cleared, ...rest } = prev;
159
+ return { ...rest, loginText: text ?? '' };
160
+ });
161
+ setCodeDraft('');
162
+ });
163
+ }, children: t('settings.submitCode') })] })), loginToast !== null && (_jsx(Toast, { text: t('settings.loginSuccess', { harness: harnessLabel(harness.id) }), icon: _jsx(IconCheckOutlineMedium, {}), onDone: () => { setLoginToast(null); } }, loginToast.seq))] }));
164
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Harness auth-status bus for the settings cards: the ProviderAuthBlock
3
+ * publishes every probe result, and card headers subscribe for the at-a-glance
4
+ * credential dot — one probe feeds both the block and the header, a login or
5
+ * logout inside any card flips every dot immediately, and every header mount
6
+ * re-probes so a mid-session credential loss cannot leave a stale green dot.
7
+ * @module @khorsheed/dsh-local-agent/client — auth status bus
8
+ */
9
+ import type { LocalAgentStatus } from '@khorsheed/dsh-local-agent/types';
10
+ /** The probe-result kinds the credential dot distinguishes. */
11
+ export type HarnessAuthStatusKind = 'checking' | 'authenticated' | 'anonymous' | 'unavailable';
12
+ /** Record one harness's latest probe result and notify subscribers. */
13
+ export declare function publishAuthStatus(harnessId: string, status: HarnessAuthStatusKind): void;
14
+ /** Test hook: clear every published status so specs start with an empty bus. */
15
+ export declare function resetAuthStatuses(): void;
16
+ /** Read one harness's latest published status without subscribing (tests, non-React callers). */
17
+ export declare function readAuthStatus(harnessId: string): HarnessAuthStatusKind | undefined;
18
+ /**
19
+ * Read one harness's latest known auth status. Every mount probes once and
20
+ * publishes — a published value only snapshots the past (a credential expiring
21
+ * mid-session must flip the dot at the next view, not stay green forever);
22
+ * the bus dedupes identical values, so repeat probes never re-render.
23
+ * @param harnessId - the harness to read.
24
+ * @param probe - the card's injected status face (read-only Remote channel).
25
+ * @returns the latest known status kind.
26
+ */
27
+ export declare function useHarnessAuthStatus(harnessId: string, probe: (name: string) => Promise<LocalAgentStatus | undefined>): HarnessAuthStatusKind;
28
+ //# sourceMappingURL=auth-status.d.ts.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Harness auth-status bus for the settings cards: the ProviderAuthBlock
3
+ * publishes every probe result, and card headers subscribe for the at-a-glance
4
+ * credential dot — one probe feeds both the block and the header, a login or
5
+ * logout inside any card flips every dot immediately, and every header mount
6
+ * re-probes so a mid-session credential loss cannot leave a stale green dot.
7
+ * @module @khorsheed/dsh-local-agent/client — auth status bus
8
+ */
9
+ import { useEffect, useSyncExternalStore } from 'react';
10
+ const statuses = new Map();
11
+ const listeners = new Set();
12
+ /** Record one harness's latest probe result and notify subscribers. */
13
+ export function publishAuthStatus(harnessId, status) {
14
+ if (statuses.get(harnessId) === status)
15
+ return;
16
+ statuses.set(harnessId, status);
17
+ for (const listener of listeners)
18
+ listener();
19
+ }
20
+ function subscribe(listener) {
21
+ listeners.add(listener);
22
+ return () => { listeners.delete(listener); };
23
+ }
24
+ /** Test hook: clear every published status so specs start with an empty bus. */
25
+ export function resetAuthStatuses() {
26
+ statuses.clear();
27
+ }
28
+ /** Read one harness's latest published status without subscribing (tests, non-React callers). */
29
+ export function readAuthStatus(harnessId) {
30
+ return statuses.get(harnessId);
31
+ }
32
+ /**
33
+ * Read one harness's latest known auth status. Every mount probes once and
34
+ * publishes — a published value only snapshots the past (a credential expiring
35
+ * mid-session must flip the dot at the next view, not stay green forever);
36
+ * the bus dedupes identical values, so repeat probes never re-render.
37
+ * @param harnessId - the harness to read.
38
+ * @param probe - the card's injected status face (read-only Remote channel).
39
+ * @returns the latest known status kind.
40
+ */
41
+ export function useHarnessAuthStatus(harnessId, probe) {
42
+ const status = useSyncExternalStore(subscribe, () => statuses.get(harnessId) ?? 'checking');
43
+ useEffect(() => {
44
+ let cancelled = false;
45
+ void probe(harnessId).then((result) => {
46
+ if (cancelled)
47
+ return;
48
+ publishAuthStatus(harnessId, result === undefined ? 'unavailable' : result.authenticated ? 'authenticated' : 'anonymous');
49
+ }).catch(() => {
50
+ if (!cancelled)
51
+ publishAuthStatus(harnessId, 'unavailable');
52
+ });
53
+ return () => { cancelled = true; };
54
+ }, [harnessId, probe]);
55
+ return status;
56
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Generated by scripts/sync-icon-artwork.mts from @deepseek-ai/dsh-client-ui-primitives
3
+ * (v0.1.7-rc.1, harness 46a7f68b09) — do not edit by hand. The 0.1.5 and rc.1 host lines
4
+ * share no icon export name, so the rc.1 artwork is inlined here: pure SVG,
5
+ * no host runtime identity, identical on every line. Re-run the generator
6
+ * after adding an icon import or bumping the harness checkout.
7
+ *
8
+ * @module @khorsheed/dsh-local-agent/client
9
+ */
10
+ /** Shared props for every product icon component (mirrors the upstream IconProps). */
11
+ export interface IconProps {
12
+ /** Square edge in px; defaults to the glyph's own drawn size. */
13
+ size?: number | undefined;
14
+ /** Extra class for layout placement; color rides currentColor. */
15
+ className?: string | undefined;
16
+ }
17
+ export declare const IconCheckOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
18
+ export declare const IconChevronDownOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
19
+ export declare const IconChevronUpOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
20
+ export declare const IconQueueOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
21
+ export declare const IconTrashOutlineMedium: ({ size, className }: IconProps) => import("react").JSX.Element;
22
+ //# sourceMappingURL=icons.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const IconCheckOutlineMedium = ({ size = 16, className }) => (_jsx("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: _jsx("path", { d: "M2.25 8.5L5.49732 11.7473C5.90519 12.1552 6.57263 12.1344 6.95426 11.7018L13.75 4", stroke: "currentColor" }) }));
3
+ export const IconChevronDownOutlineMedium = ({ size = 14, className }) => (_jsx("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: _jsx("path", { d: "M4 6L7.29289 9.29289C7.68342 9.68342 8.31658 9.68342 8.70711 9.29289L12 6", stroke: "currentColor" }) }));
4
+ export const IconChevronUpOutlineMedium = ({ size = 14, className }) => (_jsx("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: _jsx("path", { d: "M12 10L8.70711 6.70711C8.31658 6.31658 7.68342 6.31658 7.29289 6.70711L4 10", stroke: "currentColor" }) }));
5
+ export const IconQueueOutlineMedium = ({ size = 14, className }) => (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: [_jsx("path", { d: "M5 6.75H11", stroke: "currentColor" }), _jsx("path", { d: "M5 9H8", stroke: "currentColor" }), _jsx("path", { d: "M2.37067 11.2497C1.5872 9.89252 1.32042 8.29798 1.61945 6.7597C1.91847 5.22141 2.76317 3.84293 3.99801 2.87809C5.23285 1.91325 6.7747 1.427 8.33964 1.50888C9.90458 1.59076 11.3873 2.23526 12.5147 3.32369C13.6422 4.41232 14.3384 5.8717 14.4751 7.43304C14.6118 8.99438 14.1797 10.5525 13.2585 11.8205C12.3372 13.0885 10.9889 13.9809 9.4617 14.3334C8.18666 14.6277 6.8587 14.529 5.64964 14.0601C5.17095 13.8745 4.76937 13.4929 4.26509 13.3963C3.67389 13.2832 2.95232 13.5595 2.0377 14.3334", stroke: "currentColor" })] }));
6
+ export const IconTrashOutlineMedium = ({ size = 16, className }) => (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", strokeWidth: 1.3, children: [_jsx("path", { d: "M1.28149 3.88831H14.7187", stroke: "currentColor" }), _jsx("path", { d: "M5.41602 3.88833V2.47962C5.41602 2.29282 5.52492 2.11366 5.71876 1.98157C5.9126 1.84948 6.17551 1.77527 6.44964 1.77527H9.55053C9.82466 1.77527 10.0876 1.84948 10.2814 1.98157C10.4753 2.11366 10.5842 2.29282 10.5842 2.47962V3.88833", stroke: "currentColor" }), _jsx("path", { d: "M2.57349 3.88831L3.19366 13.2943C3.21937 13.5502 3.33952 13.7872 3.53065 13.9593C3.72178 14.1313 3.97016 14.2259 4.22729 14.2246H11.7728C12.0299 14.2259 12.2783 14.1313 12.4694 13.9593C12.6605 13.7872 12.7807 13.5502 12.8064 13.2943L13.4266 3.88831", stroke: "currentColor" }), _jsx("path", { d: "M6.44946 6.98926V11.1238", stroke: "currentColor" }), _jsx("path", { d: "M9.55054 6.98926V11.1238", stroke: "currentColor" })] }));
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Local-agent records plugin, browser half: the member composer for delegated
3
+ * CLI sessions, plus the shared settings-card building blocks the provider
4
+ * packages compose (ProviderAuthBlock, the auth-status bus, AuthStatusDot).
5
+ * The per-harness auth/live settings live in each provider's own settings
6
+ * card — on alpha.2 the bundle's configuration on its Plugins-page detail
7
+ * view (`plugins.bundle.config`), on 0.1.5 the `settings.plugin.item` card
8
+ * (Plugins → 可配置插件); the standalone「本地 Agent」section was retired
9
+ * once the cards carried auth (the live-settings proposal's M3). Roster and status ride the read-only local-agent Remote
10
+ * channel (no session events); only the user-initiated login/logout/preset
11
+ * commands go through the commands Remote, whose visible command node is the
12
+ * expected feedback. Delegation records live with the shared subagent
13
+ * surface — there is no separate header dropdown, so kimi delegations appear
14
+ * under the standard 子代理 list like every other subagent. The plugin holds
15
+ * no host data — every open pulls fresh.
16
+ */
17
+ import { type ReactNode } from 'react';
18
+ import { type HarnessModelPickerProps } from './HarnessModelPicker.tsx';
19
+ import type { Context } from '@deepseek-ai/cordis';
20
+ import type { TypertRemoteNamespaceMap } from '@deepseek-ai/dsh-typert-protocol';
21
+ import { type LocalAgentKey } from './locales.ts';
22
+ export type { LocalAgentHarnessView } from './LocalAgentRecordsAction.tsx';
23
+ export { ProviderAuthBlock } from './ProviderAuthBlock.tsx';
24
+ export type { ProviderAuthBlockProps, ProviderAuthInjected } from './ProviderAuthBlock.tsx';
25
+ export { AuthStatusDot } from './AuthStatusDot.tsx';
26
+ export type { AuthStatusDotProps } from './AuthStatusDot.tsx';
27
+ export { publishAuthStatus, useHarnessAuthStatus, resetAuthStatuses, readAuthStatus } from './auth-status.ts';
28
+ export type { HarnessAuthStatusKind } from './auth-status.ts';
29
+ export type { MemberComposerInjected, MemberComposerMatch, MemberComposerProps } from './MemberComposer.tsx';
30
+ export { selectCliMember } from './MemberComposer.tsx';
31
+ /** The mounted local-agent gateway namespace, read back from the global store. */
32
+ export type LocalAgentGatewayRemote = TypertRemoteNamespaceMap['localAgentGateway'];
33
+ /** Optional companion-facing renderer; consumers probe the service at gesture time. */
34
+ export type HarnessModelPickerInput = Omit<HarnessModelPickerProps, 'face' | 't'>;
35
+ export interface LocalAgentUi {
36
+ renderMemberOutput(childSessionId: string, startedAt: number): ReactNode;
37
+ renderMemberInbox(childSessionId: string): ReactNode;
38
+ renderHarnessModelPicker(name: string, props: HarnessModelPickerInput): ReactNode;
39
+ renderMemberConfiguration(childSessionId: string): ReactNode;
40
+ }
41
+ declare module '@deepseek-ai/cordis' {
42
+ interface Context {
43
+ localAgentUi: LocalAgentUi;
44
+ }
45
+ }
46
+ export { ModelConfigurationFields } from './ModelConfigurationFields.tsx';
47
+ export type { ConfigurationTranslate } from './ModelConfigurationFields.tsx';
48
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
49
+ interface LocaleNamespaceMap {
50
+ /** The local-agent settings section copy. */
51
+ 'local-agent': LocalAgentKey;
52
+ }
53
+ }
54
+ /** Required services: slot registry, the Remote channel, and locale registry. */
55
+ export declare const inject: string[];
56
+ /**
57
+ * Client plugin body: mount the local-agent Remote channel, register the
58
+ * dictionaries, and put the member composer on the conversation chain.
59
+ * @param ctx - client root context.
60
+ */
61
+ export declare function apply(ctx: Context): Promise<() => Promise<void>>;
62
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,135 @@
1
+ import { MemberFeeds } from './member-feed.js';
2
+ /**
3
+ * Local-agent records plugin, browser half: the member composer for delegated
4
+ * CLI sessions, plus the shared settings-card building blocks the provider
5
+ * packages compose (ProviderAuthBlock, the auth-status bus, AuthStatusDot).
6
+ * The per-harness auth/live settings live in each provider's own settings
7
+ * card — on alpha.2 the bundle's configuration on its Plugins-page detail
8
+ * view (`plugins.bundle.config`), on 0.1.5 the `settings.plugin.item` card
9
+ * (Plugins → 可配置插件); the standalone「本地 Agent」section was retired
10
+ * once the cards carried auth (the live-settings proposal's M3). Roster and status ride the read-only local-agent Remote
11
+ * channel (no session events); only the user-initiated login/logout/preset
12
+ * commands go through the commands Remote, whose visible command node is the
13
+ * expected feedback. Delegation records live with the shared subagent
14
+ * surface — there is no separate header dropdown, so kimi delegations appear
15
+ * under the standard 子代理 list like every other subagent. The plugin holds
16
+ * no host data — every open pulls fresh.
17
+ */
18
+ import { createElement } from 'react';
19
+ import { HarnessModelPicker } from './HarnessModelPicker.js';
20
+ import { MemberConfiguration } from './MemberConfiguration.js';
21
+ import { MemberConfigurationStores } from './member-configuration.js';
22
+ import localAgentRemote from '@khorsheed/dsh-local-agent/remote';
23
+ import { MemberComposer, selectCliMember } from './MemberComposer.js';
24
+ import { en, NS, zh } from './locales.js';
25
+ import { memberLiveDefinition } from './live-node.js';
26
+ import { MemberLiveNode, MemberLiveOutputView } from './MemberLiveNode.js';
27
+ import { MemberLiveOutputs } from './live-output.js';
28
+ export { ProviderAuthBlock } from './ProviderAuthBlock.js';
29
+ export { AuthStatusDot } from './AuthStatusDot.js';
30
+ export { publishAuthStatus, useHarnessAuthStatus, resetAuthStatuses, readAuthStatus } from './auth-status.js';
31
+ export { selectCliMember } from './MemberComposer.js';
32
+ import { MemberInboxView } from './MemberInboxView.js';
33
+ export { ModelConfigurationFields } from './ModelConfigurationFields.js';
34
+ /** Required services: slot registry, the Remote channel, and locale registry. */
35
+ export const inject = ['slots', 'remote', 'remote.commands', 'locale'];
36
+ /**
37
+ * Client plugin body: mount the local-agent Remote channel, register the
38
+ * dictionaries, and put the member composer on the conversation chain.
39
+ * @param ctx - client root context.
40
+ */
41
+ export async function apply(ctx) {
42
+ const disposers = [];
43
+ try {
44
+ // The namespace is registered by $mount; `ctx.remote.localAgentGateway`
45
+ // cannot see it (the property proxy walks the fiber chain, and the
46
+ // namespace lives in the sibling fiber $mount spawned), so read it from
47
+ // the global store once the mount has settled.
48
+ disposers.push(await ctx.remote.$mount(localAgentRemote));
49
+ }
50
+ catch (error) {
51
+ // A Remote already mounted by another composition fails loud at boot; the
52
+ // rest of the plugin still registers (callers would answer an unmounted
53
+ // namespace with a typed RPC error, which the surfaces render).
54
+ /* v8 ignore next -- double-mount is a composition error, not a runtime path */
55
+ ctx.logger.error(error);
56
+ }
57
+ const gateway = ctx.get('remote.localAgentGateway');
58
+ const unwrap = (result) => {
59
+ if (!result.ok)
60
+ throw new Error('Configuration request failed');
61
+ return result.value;
62
+ };
63
+ const feeds = new MemberFeeds((requests, signal) => gateway.followMembers(requests, signal));
64
+ ctx.effect(() => () => feeds.dispose(), 'local-agent: shared member feed');
65
+ const configurations = new MemberConfigurationStores({
66
+ read: async (id) => unwrap(await gateway.memberConfiguration(id)),
67
+ follow: (id, signal) => feeds.follow(id, 'configuration', signal),
68
+ directory: async (id, refresh) => unwrap(await gateway.memberDirectory(id, refresh)),
69
+ followDirectory: (id, signal) => feeds.follow(id, 'directory', signal),
70
+ select: async (id, request, revision, selection) => unwrap(await gateway.selectMemberConfiguration(id, request, revision, selection)),
71
+ cancel: async (id, request, revision) => unwrap(await gateway.cancelMemberConfiguration(id, request, revision)),
72
+ retry: async (id, revision) => { unwrap(await gateway.retryMemberConfiguration(id, revision)); },
73
+ });
74
+ const directoryFaces = new Map();
75
+ const directoryFace = (name) => {
76
+ let face = directoryFaces.get(name);
77
+ if (face === undefined)
78
+ directoryFaces.set(name, face = {
79
+ read: async (refresh) => unwrap(await gateway.modelDirectory(name, undefined, refresh)),
80
+ follow: signal => gateway.followModelDirectory(name, undefined, signal),
81
+ });
82
+ return face;
83
+ };
84
+ const inboxFaces = new Map();
85
+ const inboxFace = (id) => {
86
+ let face = inboxFaces.get(id);
87
+ if (face === undefined)
88
+ inboxFaces.set(id, face = {
89
+ read: async () => unwrap(await gateway.memberInbox(id)),
90
+ control: async (action, requestId, outcome, evidence) => unwrap(await gateway.controlMemberInbox(id, action, requestId, outcome, evidence)),
91
+ });
92
+ return face;
93
+ };
94
+ const outputs = new MemberLiveOutputs((id, signal) => feeds.follow(id, 'output', signal));
95
+ const configurationUi = {
96
+ renderMemberOutput: (id, startedAt) => createElement(MemberLiveOutputView, { key: `${id}:${startedAt}`, sessionId: id, startedAt, outputs, t: ctx.locale.bind(NS) }),
97
+ renderMemberInbox: id => createElement(MemberInboxView, { key: id, face: inboxFace(id), t: ctx.locale.bind(NS) }),
98
+ renderHarnessModelPicker: (name, props) => createElement(HarnessModelPicker, { ...props, key: name, face: directoryFace(name), t: ctx.locale.bind(NS) }),
99
+ renderMemberConfiguration: id => createElement(MemberConfiguration, { key: id, store: configurations.get(id), diagnostics: outputs.diagnostics, t: ctx.locale.bind(NS) }),
100
+ };
101
+ ctx.provide('localAgentUi', configurationUi);
102
+ ctx.inject(['uiConversation'], lctx => { lctx.uiConversation.events.register(memberLiveDefinition); });
103
+ ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
104
+ name: 'conversation.chat.node', key: 'local-agent-stream', locale: NS,
105
+ inject: () => ({ outputs }),
106
+ }, MemberLiveNode));
107
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-local-agent: dictionaries');
108
+ // The member composer: a one-shot subagent session that the family delegated
109
+ // gets a writable box (send = facade resume via the promptMember Remote);
110
+ // any other one-shot session gets the same read-only panel the official
111
+ // takeover would render. Priority -20 runs the selector before ui-subagent's
112
+ // read-only takeover (-10) — chain election is ascending, first non-null
113
+ // wins — and the degraded branch makes the takeover a strict subset of this
114
+ // entry, so electing first never changes a non-member session's UX.
115
+ ctx.slots.inject('conversation.composer', () => ctx.slots.register({
116
+ name: 'conversation.composer',
117
+ priority: -20,
118
+ locale: NS,
119
+ select: selectCliMember,
120
+ inject: () => ({
121
+ renderMemberConfiguration: configurationUi.renderMemberConfiguration,
122
+ renderMemberInbox: configurationUi.renderMemberInbox,
123
+ memberOf: childSessionId => gateway.memberOf(childSessionId).then(result => (result.ok ? result.value : undefined)),
124
+ promptMember: (childSessionId, text, requestId) => gateway.promptMember(childSessionId, text, requestId).then(result => (result.ok ? result.value : undefined)),
125
+ stopMember: childSessionId => gateway.stopMember(childSessionId).then(result => (result.ok ? result.value : undefined)),
126
+ activeDelegations: () => gateway.activeDelegations().then(result => (result.ok ? result.value : undefined)),
127
+ memberModel: childSessionId => gateway.memberModel(childSessionId).then(result => (result.ok ? result.value : undefined)),
128
+ setMemberModel: (childSessionId, model) => gateway.setMemberModel(childSessionId, model).then(result => (result.ok ? result.value : undefined)),
129
+ }),
130
+ }, MemberComposer));
131
+ return async () => {
132
+ for (const dispose of disposers.reverse())
133
+ await dispose();
134
+ };
135
+ }
@@ -0,0 +1,14 @@
1
+ /** Public Conversation definition: transient presentation yields to the native final message. */
2
+ import type { ConversationNodeDefinition } from '@deepseek-ai/dsh-client-ui-conversation/client';
3
+ import type { LocalAgentStreamCheckpoint } from '../types.ts';
4
+ export interface MemberLiveNodeData extends LocalAgentStreamCheckpoint {
5
+ readonly seq: number;
6
+ readonly settled: boolean;
7
+ }
8
+ declare module '@deepseek-ai/dsh-client-ui-chat/client' {
9
+ interface ChatNodeDataMap {
10
+ 'local-agent-stream': MemberLiveNodeData;
11
+ }
12
+ }
13
+ export declare const memberLiveDefinition: ConversationNodeDefinition<MemberLiveNodeData>;
14
+ //# sourceMappingURL=live-node.d.ts.map
@@ -0,0 +1,34 @@
1
+ export const memberLiveDefinition = {
2
+ kind: 'local-agent-stream',
3
+ target: 'chat',
4
+ match(event) {
5
+ if (event.type === 'local-agent/stream')
6
+ return { id: event.data.id, role: event.data.opening ? 'start' : 'update' };
7
+ if (event.type === 'assistant/message')
8
+ return { id: `${event.data.turn}:${event.data.step}`, role: 'update' };
9
+ return null;
10
+ },
11
+ start(_context, match) {
12
+ if (match.event.type !== 'local-agent/stream')
13
+ throw new Error('stream anchor required');
14
+ return { ...match.event.data, seq: match.event.seq, settled: match.event.data.closed === true };
15
+ },
16
+ update(context, match) {
17
+ if (match.event.type === 'assistant/message')
18
+ return { ...context.state, settled: true };
19
+ if (match.event.type !== 'local-agent/stream')
20
+ return context.state;
21
+ const checkpoint = match.event.data;
22
+ return { ...context.state, ...checkpoint, settled: context.state.settled || checkpoint.closed === true, text: checkpoint.append ? context.state.text + checkpoint.text : checkpoint.text };
23
+ },
24
+ buildViewNode(context) {
25
+ if (context.state === undefined)
26
+ return null;
27
+ const node = {
28
+ key: context.key, kind: 'local-agent-stream', id: context.id, target: 'chat',
29
+ anchorSeq: context.state.seq, location: context.start?.location ?? { kind: 'unresolved' },
30
+ visibility: context.state.settled ? 'hidden' : 'visible', data: context.state,
31
+ };
32
+ return node;
33
+ },
34
+ };
@@ -0,0 +1,34 @@
1
+ /** One shared Remote subscription per viewed member, independent of session history. */
2
+ import type { LocalAgentStreamFrame, LocalAgentStreamItem } from '../types.ts';
3
+ import { LivePaintDiagnostics } from './live-paint.ts';
4
+ export type FollowMemberOutput = (id: string, signal: AbortSignal) => AsyncIterable<LocalAgentStreamFrame>;
5
+ export interface LiveOutputItem extends LocalAgentStreamItem {
6
+ baseline: boolean;
7
+ }
8
+ export interface LiveOutputSnapshot {
9
+ readonly items: ReadonlyMap<string, LiveOutputItem>;
10
+ readonly connected: boolean;
11
+ }
12
+ export declare class MemberLiveOutput {
13
+ private readonly id;
14
+ private readonly follow;
15
+ private readonly onIdle;
16
+ private readonly diagnostics;
17
+ private readonly listeners;
18
+ private snapshot;
19
+ private controller;
20
+ constructor(id: string, follow: FollowMemberOutput, onIdle: () => void, diagnostics: LivePaintDiagnostics);
21
+ readonly getSnapshot: () => LiveOutputSnapshot;
22
+ readonly subscribe: (listener: () => void) => (() => void);
23
+ private publish;
24
+ private run;
25
+ }
26
+ /** Cached only while rendered: historical sessions do not retain text or connections. */
27
+ export declare class MemberLiveOutputs {
28
+ private readonly follow;
29
+ readonly diagnostics: LivePaintDiagnostics;
30
+ private readonly stores;
31
+ constructor(follow: FollowMemberOutput);
32
+ get(id: string): MemberLiveOutput;
33
+ }
34
+ //# sourceMappingURL=live-output.d.ts.map