@mind-harness/mh-browser 0.2.3 → 0.2.5

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.
@@ -44,7 +44,7 @@ const $remote = createTypertRemoteInfo({
44
44
  create: _mind_harness_mh_browser_mhBrowser_revealHandover_result$schema,
45
45
  jsonSchema: {"type":"object","properties":{"status":{"type":"string","enum":["revealed","not-handing-over","no-tab"]}},"required":["status"],"additionalProperties":true},
46
46
  },
47
- sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":624,"column":9},
47
+ sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":684,"column":9},
48
48
  },
49
49
  },
50
50
  }, z)
@@ -44,7 +44,7 @@ export const TYPERT_REMOTE = createTypertRemoteInfo({
44
44
  create: _mind_harness_mh_browser_mhBrowser_revealHandover_result$schema,
45
45
  jsonSchema: {"type":"object","properties":{"status":{"type":"string","enum":["revealed","not-handing-over","no-tab"]}},"required":["status"],"additionalProperties":true},
46
46
  },
47
- sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":624,"column":9},
47
+ sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":684,"column":9},
48
48
  },
49
49
  },
50
50
  }, z)
@@ -0,0 +1,24 @@
1
+ import { type ReactElement } from 'react';
2
+ import { type Translate } from './locales.ts';
3
+ export type RevealStatus = 'revealed' | 'not-handing-over' | 'no-tab';
4
+ export type RevealResult = {
5
+ ok: true;
6
+ value: {
7
+ status: RevealStatus;
8
+ };
9
+ } | {
10
+ ok: false;
11
+ error: {
12
+ code: string;
13
+ message: string;
14
+ };
15
+ };
16
+ export interface HandoverRowProps {
17
+ sessionId: string;
18
+ reason: string;
19
+ t: Translate;
20
+ reveal: (sessionId: string, signal: AbortSignal) => Promise<RevealResult>;
21
+ }
22
+ /** The open handover: the reason and the button that raises its window. */
23
+ export declare function HandoverRow(props: HandoverRowProps): ReactElement;
24
+ //# sourceMappingURL=card.d.ts.map
@@ -0,0 +1,53 @@
1
+ import { type ReactElement } from 'react';
2
+ import { type RevealResult } from './card.ts';
3
+ import { type Translate } from './locales.ts';
4
+ import { handoverRowDefinition } from './row.ts';
5
+ /**
6
+ * Cordis services this client reads. `dsh.client.inject` is package load
7
+ * order, not this list. `remote.mhBrowser` is absent until `$mount`.
8
+ */
9
+ export declare const inject: readonly ["slots", "locale", "remote", "uiConversation"];
10
+ export interface MhBrowserRemote {
11
+ revealHandover(sessionId: string, signal?: AbortSignal): Promise<RevealResult>;
12
+ }
13
+ interface RemoteCarrier {
14
+ $mount(contribution: object): Promise<() => Promise<void>>;
15
+ }
16
+ /** Props the session-scoped chat-node seat passes in. The session id is that seat's. */
17
+ export interface HostRowProps {
18
+ sessionId: string;
19
+ node: {
20
+ readonly data: {
21
+ readonly reason: string;
22
+ };
23
+ };
24
+ t: Translate;
25
+ }
26
+ export interface ClientHost {
27
+ slots: {
28
+ inject(name: string, register: () => unknown): unknown;
29
+ register(options: {
30
+ name: string;
31
+ key: string;
32
+ locale: string;
33
+ }, component: (props: HostRowProps) => ReactElement): () => void;
34
+ };
35
+ locale: {
36
+ register(ns: string, dicts: Record<'zh' | 'en', Record<string, string>>): () => void;
37
+ };
38
+ uiConversation: {
39
+ events: {
40
+ register(definition: typeof handoverRowDefinition): () => void;
41
+ };
42
+ };
43
+ effect(execute: () => () => void): unknown;
44
+ remote: RemoteCarrier & {
45
+ mhBrowser: MhBrowserRemote;
46
+ };
47
+ inject(names: readonly string[], apply: (ctx: ClientHost) => void): PromiseLike<unknown>;
48
+ }
49
+ export declare function apply(ctx: ClientHost): Promise<() => Promise<void>>;
50
+ /** Register the handover row. The tool call itself keeps the generic tool row. */
51
+ export declare function start(ctx: ClientHost): () => void;
52
+ export {};
53
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,15 @@
1
+ /** Dictionary namespace for the handover row. */
2
+ export declare const NS = "mh-browser";
3
+ export declare const zh: {
4
+ readonly waiting: "浏览器交给你了:{reason}";
5
+ readonly reveal: "去浏览器";
6
+ readonly notHandingOver: "这次交接已经结束";
7
+ readonly noTab: "没有可以去的页面";
8
+ readonly failed: "没能切到浏览器";
9
+ };
10
+ export type CardKey = keyof typeof zh;
11
+ export declare const en: Record<CardKey, string>;
12
+ export type Translate = (key: CardKey, params?: Record<string, unknown>) => string;
13
+ /** A translator over one dictionary, for rendering outside the host (tests). */
14
+ export declare function translator(dict: Record<CardKey, string>): Translate;
15
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * One chat row for the open `browser_handover` in a turn.
3
+ * `groupTree: 'turn'` stands outside the process group. A settled call keeps
4
+ * the same key and hides the row; a hidden row is omitted from group order.
5
+ */
6
+ export declare const HANDOVER_KIND = "browser-handover";
7
+ export interface HandoverRowData {
8
+ readonly reason: string;
9
+ }
10
+ interface OpenCall {
11
+ readonly callId: string;
12
+ readonly reason: string;
13
+ readonly seq: number;
14
+ }
15
+ export interface HandoverTurnState {
16
+ readonly open: OpenCall | undefined;
17
+ readonly anchorSeq: number | undefined;
18
+ }
19
+ /** A session event this row reads. Other fields are ignored. */
20
+ export interface FoldEvent {
21
+ readonly type: string;
22
+ readonly seq: number;
23
+ readonly data: unknown;
24
+ }
25
+ export interface HandoverMatch {
26
+ readonly id: string;
27
+ readonly role: 'start' | 'update';
28
+ }
29
+ export interface HandoverChatNode {
30
+ readonly key: string;
31
+ readonly kind: typeof HANDOVER_KIND;
32
+ readonly id: string;
33
+ readonly target: 'chat';
34
+ readonly anchorSeq: number;
35
+ readonly location: unknown;
36
+ readonly visibility: 'visible' | 'hidden';
37
+ readonly groupTree: 'turn';
38
+ readonly data: HandoverRowData;
39
+ }
40
+ export interface HandoverNodeContext {
41
+ readonly key: string;
42
+ readonly id: string;
43
+ readonly state: HandoverTurnState | undefined;
44
+ readonly start: {
45
+ readonly location: unknown;
46
+ } | undefined;
47
+ readonly current: {
48
+ get(target: string): HandoverChatNode | null | undefined;
49
+ };
50
+ }
51
+ /**
52
+ * The row for one turn. Absent until a handover in that turn has been seen;
53
+ * hidden, with the same key, once the open call settles.
54
+ */
55
+ export declare function buildHandoverView(context: HandoverNodeContext): HandoverChatNode | null;
56
+ export declare const handoverRowDefinition: {
57
+ kind: string;
58
+ target: "chat";
59
+ match(event: FoldEvent): HandoverMatch | null;
60
+ start(): HandoverTurnState;
61
+ update(context: {
62
+ readonly state: HandoverTurnState;
63
+ }, match: {
64
+ readonly event: FoldEvent;
65
+ }): HandoverTurnState;
66
+ buildViewNode(context: HandoverNodeContext): HandoverChatNode | null;
67
+ };
68
+ export {};
69
+ //# sourceMappingURL=row.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { MhBrowserError } from '../types.ts';
2
+ /** A wait ended because its signal aborted, including a deadline synthesized here. */
3
+ export declare function stopped(): MhBrowserError;
4
+ export declare function isInterrupted(error: unknown): boolean;
5
+ /**
6
+ * `promise`, or a rejection when `signal` aborts. The original promise is
7
+ * always observed, so abandoning it leaves no unhandled rejection.
8
+ */
9
+ export declare function untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;
10
+ /** `signal`, a deadline of `ms`, or whichever of the two aborts first. */
11
+ export declare function capped(signal: AbortSignal | undefined, ms: number): AbortSignal;
12
+ export interface Queued<T> {
13
+ /** Rejects as soon as `signal` aborts, including while `previous` is still running. */
14
+ run: Promise<T>;
15
+ /** Settles after `previous`, and after `op` when `op` started. Never rejects. */
16
+ tail: Promise<void>;
17
+ }
18
+ /**
19
+ * Run `op` once `previous` settles. Aborting `signal` while waiting rejects
20
+ * `run` and does not call `op`. `tail` still follows `previous`, and `op`
21
+ * when `op` has started, so a later caller cannot overlap the work.
22
+ */
23
+ export declare function enqueue<T>(previous: Promise<unknown>, op: () => Promise<T>, signal?: AbortSignal): Queued<T>;
24
+ //# sourceMappingURL=abortable.d.ts.map
@@ -17,16 +17,17 @@ export declare class Extension {
17
17
  /** Address of a page shipped in the extension directory. */
18
18
  pageUrl(name: string): string;
19
19
  /** `chrome.tabs.create` in the named window. Returns the extension's tab id. */
20
- createTab(windowId: number, url: string): Promise<number>;
21
- tabIdsIn(windowId: number): Promise<number[]>;
22
- activate(tabId: number): Promise<void>;
20
+ createTab(windowId: number, url: string, signal?: AbortSignal): Promise<number>;
21
+ tabIdsIn(windowId: number, signal?: AbortSignal): Promise<number[]>;
22
+ activate(tabId: number, signal?: AbortSignal): Promise<void>;
23
23
  group(tabId: number, target: {
24
24
  groupId: number;
25
25
  } | {
26
26
  windowId: number;
27
- }): Promise<number>;
28
- styleGroup(groupId: number, title: string, color: 'blue' | 'red'): Promise<void>;
29
- ungroup(tabId: number): Promise<void>;
27
+ }, signal?: AbortSignal): Promise<number>;
28
+ styleGroup(groupId: number, title: string, color: 'blue' | 'red', signal?: AbortSignal): Promise<void>;
29
+ ungroup(tabId: number, signal?: AbortSignal): Promise<void>;
30
+ private answer;
30
31
  private worker;
31
32
  }
32
33
  /** The extension's service worker. Looks once more after the wait times out. */
@@ -114,7 +114,7 @@ export declare class Windows {
114
114
  /** The tab touched most recently, for raising the window the person asked to see. */
115
115
  latest(owner: OwnerId): LiveTab | undefined;
116
116
  /** Make this tab the current one in its window. Does not raise the browser. */
117
- activate(tab: LiveTab): Promise<void>;
117
+ activate(tab: LiveTab, signal?: AbortSignal): Promise<void>;
118
118
  /** Group title for the person. Empty until a title is known. */
119
119
  setTitle(owner: OwnerId, title: string): void;
120
120
  /** A handover in progress prefixes the group title and recolors it. */
@@ -126,15 +126,17 @@ export declare class Windows {
126
126
  * use the extension. Without a window, later pages use `window.open` from
127
127
  * an isolated world so they stay in that window.
128
128
  */
129
- openBlank(owner: OwnerId): Promise<LiveTab>;
129
+ openBlank(owner: OwnerId, signal?: AbortSignal): Promise<LiveTab>;
130
130
  private createTab;
131
131
  private keep;
132
+ /** The page from a create, or close it when this wait is abandoned. */
133
+ private claimedPage;
132
134
  /**
133
135
  * Close one tab. `note` is listed in the next `<tabs>` block. A tab the
134
136
  * model was never shown (an Agent View discovered on a new tab) omits it.
135
137
  */
136
138
  closeTab(tab: LiveTab, note?: string): Promise<void>;
137
- closeOwner(owner: OwnerId): Promise<void>;
139
+ closeOwner(owner: OwnerId, signal?: AbortSignal): Promise<void>;
138
140
  private closePage;
139
141
  /** A tab leaves the registry. The window closes with its last tab. */
140
142
  private forget;
@@ -173,7 +175,13 @@ export declare class Windows {
173
175
  private arm;
174
176
  private report;
175
177
  private waitForTarget;
178
+ private dropSession;
176
179
  private targetIdOf;
177
180
  private windowIdOf;
181
+ private pageSession;
182
+ private cdp;
183
+ /** Close a page that may no longer answer. A stall ends at `CLOSE_MS`. */
184
+ private shutdown;
185
+ private shutdownSession;
178
186
  }
179
187
  //# sourceMappingURL=windows.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** A string the caller gave. Blank is the same as omitting it; the text itself is kept. */
2
+ export declare function givenString(value: unknown): string | undefined;
3
+ /** Seconds to wait. Zero is the same as omitting it; any other number is kept. */
4
+ export declare function givenTime(value: unknown): number | undefined;
5
+ //# sourceMappingURL=given.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mind-harness/mh-browser",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Opens pages in a real browser for the agent, keeps chosen Agent Views in context, and hands the browser to you when a person is needed.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "files": [
@@ -11,9 +11,11 @@
11
11
  "lib/typert.host.d.ts",
12
12
  "lib/typert.remote-client.js",
13
13
  "lib/typert.remote-client.d.ts",
14
+ "lib/client.cjs",
14
15
  "locale/*.json",
15
16
  "icon.svg",
16
- "extension/*"
17
+ "extension/*",
18
+ "THIRD-PARTY-NOTICES"
17
19
  ],
18
20
  "type": "module",
19
21
  "main": "./lib/index.js",
@@ -35,6 +37,10 @@
35
37
  "types": "./lib/typert.remote-client.d.ts",
36
38
  "default": "./lib/typert.remote-client.js"
37
39
  },
40
+ "./client": {
41
+ "types": "./lib/types/client.d.ts",
42
+ "default": "./lib/client.cjs"
43
+ },
38
44
  "./package.json": "./package.json",
39
45
  "./locale/*.json": "./locale/*.json",
40
46
  "./icon": "./icon.svg"
@@ -63,7 +69,11 @@
63
69
  "@deepseek-ai/dsh-tools": "^0.2.2-rc.1",
64
70
  "@deepseek-ai/dsh-typert-protocol": "^0.2.2-rc.1",
65
71
  "@deepseek-ai/dsh-user-questions": "^0.2.2-rc.1",
66
- "@deepseek-ai/schemastery": "~3.19.0"
72
+ "@deepseek-ai/schemastery": "~3.19.0",
73
+ "@types/react": "^19",
74
+ "@types/react-dom": "^19",
75
+ "react": "^19.2.8",
76
+ "react-dom": "^19.2.8"
67
77
  },
68
78
  "peerDependencies": {
69
79
  "@deepseek-ai/cordis": "^4.1.0",
@@ -72,12 +82,22 @@
72
82
  "@deepseek-ai/dsh-tools": ">=0.2.2-rc.1 <0.2.3-0",
73
83
  "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-rc.1 <0.2.3-0",
74
84
  "@deepseek-ai/dsh-user-questions": ">=0.2.2-rc.1 <0.2.3-0",
75
- "@deepseek-ai/schemastery": "^3.19.0"
85
+ "@deepseek-ai/schemastery": "^3.19.0",
86
+ "react": "^19.2.8"
76
87
  },
77
88
  "icon": "./icon.svg",
78
89
  "engines": {
79
90
  "dsh": ">=0.2.2-rc.1 <0.2.3-0"
80
91
  },
92
+ "dsh": {
93
+ "client": {
94
+ "platform": "web",
95
+ "inject": [
96
+ "@deepseek-ai/dsh-client-locale",
97
+ "@deepseek-ai/dsh-client-ui-conversation"
98
+ ]
99
+ }
100
+ },
81
101
  "scripts": {
82
102
  "version": "node ../../scripts/mh/sync-extension-version.ts"
83
103
  }