@harborclient/sdk 1.5.4 → 1.6.1
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/CHANGELOG.md +14 -0
- package/dist/components/Breadcrumb/CrumbSegment.d.ts.map +1 -1
- package/dist/components/Breadcrumb/CrumbSegment.js +4 -2
- package/dist/components/Breadcrumb/types.d.ts +10 -1
- package/dist/components/Breadcrumb/types.d.ts.map +1 -1
- package/dist/components/MethodSelect/MethodSelectMenu.d.ts +34 -0
- package/dist/components/MethodSelect/MethodSelectMenu.d.ts.map +1 -0
- package/dist/components/MethodSelect/MethodSelectMenu.js +150 -0
- package/dist/components/MethodSelect/MethodSelectOption.d.ts +36 -0
- package/dist/components/MethodSelect/MethodSelectOption.d.ts.map +1 -0
- package/dist/components/MethodSelect/MethodSelectOption.js +15 -0
- package/dist/components/MethodSelect/MethodSelectSeparator.d.ts +10 -0
- package/dist/components/MethodSelect/MethodSelectSeparator.d.ts.map +1 -0
- package/dist/components/MethodSelect/MethodSelectSeparator.js +11 -0
- package/dist/components/MethodSelect/index.d.ts +21 -7
- package/dist/components/MethodSelect/index.d.ts.map +1 -1
- package/dist/components/MethodSelect/index.js +47 -6
- package/dist/components/SegmentedTabs/index.d.ts +7 -2
- package/dist/components/SegmentedTabs/index.d.ts.map +1 -1
- package/dist/components/SegmentedTabs/index.js +7 -2
- package/dist/components/SettingIdLabel/index.d.ts +8 -1
- package/dist/components/SettingIdLabel/index.d.ts.map +1 -1
- package/dist/components/SettingIdLabel/index.js +5 -2
- package/dist/components/SidebarItem/sidebarItemClasses.d.ts.map +1 -1
- package/dist/components/SidebarItem/sidebarItemClasses.js +2 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/runtime/createBridgedPluginContext.js +201 -5
- package/dist/runtime/viewHost.js +4 -0
- package/dist/snippets.d.ts +30 -19
- package/dist/types.d.ts +284 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/tokens.d.ts +1 -0
- package/dist/ui/tokens.d.ts.map +1 -1
- package/dist/ui/tokens.js +4 -2
- package/package.json +4 -2
|
@@ -28,6 +28,28 @@ let importRegistrationCounter = 0;
|
|
|
28
28
|
/** Monotonic id generator for MCP server registrations within one webview. */
|
|
29
29
|
let mcpRegistrationCounter = 0;
|
|
30
30
|
let aiChatPointerRegistrationCounter = 0;
|
|
31
|
+
let aiInstructionsRegistrationCounter = 0;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Before-turn handlers registered via `hc.ai.onBeforeTurn`.
|
|
35
|
+
*
|
|
36
|
+
* @type {Set<(ctx: import('../types').PluginAiBeforeTurnContext) => void | Promise<void>>}
|
|
37
|
+
*/
|
|
38
|
+
const aiBeforeTurnHandlers = new Set();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* After-turn handlers registered via `hc.ai.onAfterTurn`.
|
|
42
|
+
*
|
|
43
|
+
* @type {Set<(ctx: import('../types').PluginAiAfterTurnContext) => void | Promise<void>>}
|
|
44
|
+
*/
|
|
45
|
+
const aiAfterTurnHandlers = new Set();
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Local instruction texts keyed by registration id (for `hc.ai.instructions.list`).
|
|
49
|
+
*
|
|
50
|
+
* @type {Map<string, string>}
|
|
51
|
+
*/
|
|
52
|
+
const aiInstructionsByRegistrationId = new Map();
|
|
31
53
|
|
|
32
54
|
/** Plugin id prefix for built-in HarborClient host commands executed in the renderer. */
|
|
33
55
|
const HOST_COMMAND_OWNER = 'harborclient';
|
|
@@ -38,6 +60,12 @@ let importInvokeListenerInstalled = false;
|
|
|
38
60
|
/** Guards repeated chat-pointer parse listener installation per webview load. */
|
|
39
61
|
let aiParseChatPointerListenerInstalled = false;
|
|
40
62
|
|
|
63
|
+
/** Guards repeated before-turn listener installation per webview load. */
|
|
64
|
+
let aiBeforeTurnListenerInstalled = false;
|
|
65
|
+
|
|
66
|
+
/** Guards repeated after-turn listener installation per webview load. */
|
|
67
|
+
let aiAfterTurnListenerInstalled = false;
|
|
68
|
+
|
|
41
69
|
/**
|
|
42
70
|
* Normalizes a file extension to lowercase with a leading dot.
|
|
43
71
|
*
|
|
@@ -133,7 +161,11 @@ export function installImportInvokeListener() {
|
|
|
133
161
|
export function resetMcpServersForTests() {
|
|
134
162
|
mcpRegistrationCounter = 0;
|
|
135
163
|
aiChatPointerRegistrationCounter = 0;
|
|
164
|
+
aiInstructionsRegistrationCounter = 0;
|
|
136
165
|
chatPointerParseByRegistrationId.clear();
|
|
166
|
+
aiInstructionsByRegistrationId.clear();
|
|
167
|
+
aiBeforeTurnHandlers.clear();
|
|
168
|
+
aiAfterTurnHandlers.clear();
|
|
137
169
|
}
|
|
138
170
|
|
|
139
171
|
/**
|
|
@@ -144,6 +176,8 @@ export function resetImportHandlersForTests() {
|
|
|
144
176
|
importRegistrationCounter = 0;
|
|
145
177
|
importInvokeListenerInstalled = false;
|
|
146
178
|
aiParseChatPointerListenerInstalled = false;
|
|
179
|
+
aiBeforeTurnListenerInstalled = false;
|
|
180
|
+
aiAfterTurnListenerInstalled = false;
|
|
147
181
|
resetMcpServersForTests();
|
|
148
182
|
}
|
|
149
183
|
|
|
@@ -211,6 +245,113 @@ export function installAiParseChatPointerListener() {
|
|
|
211
245
|
});
|
|
212
246
|
}
|
|
213
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Subscribes to host-initiated before-turn invocations for the agent webview.
|
|
250
|
+
*
|
|
251
|
+
* Must run once before plugin activation so chat sends can reach registered handlers.
|
|
252
|
+
*/
|
|
253
|
+
export function installAiBeforeTurnListener() {
|
|
254
|
+
if (aiBeforeTurnListenerInstalled) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
aiBeforeTurnListenerInstalled = true;
|
|
258
|
+
|
|
259
|
+
bridgeOn('ai.beforeTurn', async (payload) => {
|
|
260
|
+
const { requestId, chatId, model, hubId, userMessage, messages } = payload ?? {};
|
|
261
|
+
if (requestId == null) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const extraInstructions = [];
|
|
267
|
+
let cancelled = false;
|
|
268
|
+
let cancelReason;
|
|
269
|
+
const userMessageState = {
|
|
270
|
+
content: String(userMessage?.content ?? ''),
|
|
271
|
+
...(userMessage?.referenceSnapshots != null
|
|
272
|
+
? { referenceSnapshots: userMessage.referenceSnapshots }
|
|
273
|
+
: {})
|
|
274
|
+
};
|
|
275
|
+
/** @type {import('../types').PluginAiBeforeTurnContext} */
|
|
276
|
+
const ctx = {
|
|
277
|
+
chatId: Number(chatId) || 0,
|
|
278
|
+
model: String(model ?? ''),
|
|
279
|
+
...(hubId != null && String(hubId).trim() !== '' ? { hubId: String(hubId) } : {}),
|
|
280
|
+
userMessage: userMessageState,
|
|
281
|
+
instructions: {
|
|
282
|
+
push: (text) => {
|
|
283
|
+
const trimmed = String(text ?? '').trim();
|
|
284
|
+
if (trimmed) {
|
|
285
|
+
extraInstructions.push(trimmed);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
get list() {
|
|
289
|
+
return [...extraInstructions];
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
messages: Array.isArray(messages)
|
|
293
|
+
? messages.map((row) => ({
|
|
294
|
+
role: row?.role ?? 'user',
|
|
295
|
+
content: row?.content ?? null
|
|
296
|
+
}))
|
|
297
|
+
: [],
|
|
298
|
+
cancel: (reason) => {
|
|
299
|
+
cancelled = true;
|
|
300
|
+
if (reason != null && String(reason).trim() !== '') {
|
|
301
|
+
cancelReason = String(reason).trim();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
for (const handler of [...aiBeforeTurnHandlers]) {
|
|
307
|
+
await handler(ctx);
|
|
308
|
+
if (cancelled) {
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await bridgeInvoke('ai.beforeTurnComplete', {
|
|
314
|
+
requestId,
|
|
315
|
+
ok: true,
|
|
316
|
+
result: {
|
|
317
|
+
cancelled,
|
|
318
|
+
...(cancelReason != null ? { cancelReason } : {}),
|
|
319
|
+
userContent: ctx.userMessage.content,
|
|
320
|
+
extraInstructions
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
} catch (error) {
|
|
324
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
325
|
+
await bridgeInvoke('ai.beforeTurnComplete', {
|
|
326
|
+
requestId,
|
|
327
|
+
ok: false,
|
|
328
|
+
error: message
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Subscribes to host-initiated after-turn push events for the agent webview.
|
|
336
|
+
*
|
|
337
|
+
* Must run once before plugin activation.
|
|
338
|
+
*/
|
|
339
|
+
export function installAiAfterTurnListener() {
|
|
340
|
+
if (aiAfterTurnListenerInstalled) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
aiAfterTurnListenerInstalled = true;
|
|
344
|
+
|
|
345
|
+
bridgeOn('ai.afterTurn', (payload) => {
|
|
346
|
+
const ctx = /** @type {import('../types').PluginAiAfterTurnContext} */ (payload);
|
|
347
|
+
for (const handler of [...aiAfterTurnHandlers]) {
|
|
348
|
+
void Promise.resolve(handler(ctx)).catch((error) => {
|
|
349
|
+
console.error('Plugin ai.onAfterTurn handler failed:', error);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
214
355
|
/**
|
|
215
356
|
* Normalizes one MCP header row from plugin registration input.
|
|
216
357
|
*
|
|
@@ -414,6 +555,11 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
414
555
|
*/
|
|
415
556
|
const assertUi = () => assertPermission('ui');
|
|
416
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Asserts network permission for outbound HTTP via hc.host.fetch.
|
|
560
|
+
*/
|
|
561
|
+
const assertNetwork = () => assertPermission('network');
|
|
562
|
+
|
|
417
563
|
/**
|
|
418
564
|
* Returns whether UI registration should run in this webview role.
|
|
419
565
|
*/
|
|
@@ -1156,9 +1302,9 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
1156
1302
|
}
|
|
1157
1303
|
});
|
|
1158
1304
|
},
|
|
1159
|
-
|
|
1305
|
+
send: async () => {
|
|
1160
1306
|
assertUi();
|
|
1161
|
-
await bridgeInvoke('host.
|
|
1307
|
+
await bridgeInvoke('host.send');
|
|
1162
1308
|
},
|
|
1163
1309
|
createEnvironmentWithVariables: async (name, variables) => {
|
|
1164
1310
|
assertUi();
|
|
@@ -1330,9 +1476,9 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
1330
1476
|
assertUi();
|
|
1331
1477
|
await bridgeInvoke('host.logRequestToConsole', { payload });
|
|
1332
1478
|
},
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
return bridgeInvoke('host.
|
|
1479
|
+
fetch: async (input, init) => {
|
|
1480
|
+
assertNetwork();
|
|
1481
|
+
return bridgeInvoke('host.fetch', { input, init });
|
|
1336
1482
|
},
|
|
1337
1483
|
clearResponse: async () => {
|
|
1338
1484
|
assertUi();
|
|
@@ -1519,6 +1665,56 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
1519
1665
|
context: String(input?.context ?? ''),
|
|
1520
1666
|
selection: input?.selection
|
|
1521
1667
|
});
|
|
1668
|
+
},
|
|
1669
|
+
instructions: {
|
|
1670
|
+
add: (text) => {
|
|
1671
|
+
assertAi();
|
|
1672
|
+
if (!isAgent) {
|
|
1673
|
+
return noopDisposable();
|
|
1674
|
+
}
|
|
1675
|
+
const trimmed = String(text ?? '').trim();
|
|
1676
|
+
const registrationId = String(++aiInstructionsRegistrationCounter);
|
|
1677
|
+
if (trimmed) {
|
|
1678
|
+
aiInstructionsByRegistrationId.set(registrationId, trimmed);
|
|
1679
|
+
}
|
|
1680
|
+
void bridgeInvoke('ai.registerInstructions', {
|
|
1681
|
+
registrationId,
|
|
1682
|
+
text: trimmed
|
|
1683
|
+
});
|
|
1684
|
+
return track(() => {
|
|
1685
|
+
aiInstructionsByRegistrationId.delete(registrationId);
|
|
1686
|
+
void bridgeInvoke('ai.unregisterInstructions', { registrationId });
|
|
1687
|
+
});
|
|
1688
|
+
},
|
|
1689
|
+
get list() {
|
|
1690
|
+
return [...aiInstructionsByRegistrationId.values()];
|
|
1691
|
+
}
|
|
1692
|
+
},
|
|
1693
|
+
onBeforeTurn: (handler) => {
|
|
1694
|
+
assertAi();
|
|
1695
|
+
if (!isAgent) {
|
|
1696
|
+
return noopDisposable();
|
|
1697
|
+
}
|
|
1698
|
+
if (typeof handler !== 'function') {
|
|
1699
|
+
throw new Error('onBeforeTurn handler must be a function.');
|
|
1700
|
+
}
|
|
1701
|
+
aiBeforeTurnHandlers.add(handler);
|
|
1702
|
+
return track(() => {
|
|
1703
|
+
aiBeforeTurnHandlers.delete(handler);
|
|
1704
|
+
});
|
|
1705
|
+
},
|
|
1706
|
+
onAfterTurn: (handler) => {
|
|
1707
|
+
assertAi();
|
|
1708
|
+
if (!isAgent) {
|
|
1709
|
+
return noopDisposable();
|
|
1710
|
+
}
|
|
1711
|
+
if (typeof handler !== 'function') {
|
|
1712
|
+
throw new Error('onAfterTurn handler must be a function.');
|
|
1713
|
+
}
|
|
1714
|
+
aiAfterTurnHandlers.add(handler);
|
|
1715
|
+
return track(() => {
|
|
1716
|
+
aiAfterTurnHandlers.delete(handler);
|
|
1717
|
+
});
|
|
1522
1718
|
}
|
|
1523
1719
|
},
|
|
1524
1720
|
/**
|
package/dist/runtime/viewHost.js
CHANGED
|
@@ -2,6 +2,8 @@ import { clearContributionRegistry } from './contributionRegistry.js';
|
|
|
2
2
|
import {
|
|
3
3
|
createBridgedPluginContext,
|
|
4
4
|
executeLocalPluginCommand,
|
|
5
|
+
installAiAfterTurnListener,
|
|
6
|
+
installAiBeforeTurnListener,
|
|
5
7
|
installAiParseChatPointerListener,
|
|
6
8
|
installImportInvokeListener,
|
|
7
9
|
mountContributionView,
|
|
@@ -74,6 +76,8 @@ export async function bootstrapViewHost(options = {}) {
|
|
|
74
76
|
if (parsedRole.mode === 'agent') {
|
|
75
77
|
installImportInvokeListener();
|
|
76
78
|
installAiParseChatPointerListener();
|
|
79
|
+
installAiBeforeTurnListener();
|
|
80
|
+
installAiAfterTurnListener();
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
await module.activate(hc);
|
package/dist/snippets.d.ts
CHANGED
|
@@ -246,28 +246,37 @@ interface HcInfoApi {
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
/**
|
|
249
|
-
*
|
|
249
|
+
* RequestInit-compatible options accepted by hc.fetch.
|
|
250
250
|
*/
|
|
251
|
-
interface
|
|
251
|
+
interface HcFetchInit {
|
|
252
252
|
method?: string;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
params?: Array<{ key: string; value: string; enabled?: boolean }>;
|
|
256
|
-
body?: string;
|
|
257
|
-
bodyType?: 'none' | 'json' | 'text' | 'multipart' | 'urlencoded';
|
|
258
|
-
body_type?: 'none' | 'json' | 'text' | 'multipart' | 'urlencoded';
|
|
253
|
+
headers?: Record<string, string> | Array<[string, string]>;
|
|
254
|
+
body?: string | URLSearchParams | null;
|
|
259
255
|
}
|
|
260
256
|
|
|
261
257
|
/**
|
|
262
|
-
*
|
|
258
|
+
* Headers-like facade returned by hc.fetch.
|
|
263
259
|
*/
|
|
264
|
-
interface
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
260
|
+
interface HcFetchHeaders {
|
|
261
|
+
get(name: string): string | null;
|
|
262
|
+
has(name: string): boolean;
|
|
263
|
+
entries(): IterableIterator<[string, string]>;
|
|
264
|
+
keys(): IterableIterator<string>;
|
|
265
|
+
values(): IterableIterator<string>;
|
|
266
|
+
forEach(callback: (value: string, key: string) => void): void;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Response-compatible object returned by hc.fetch.
|
|
271
|
+
*/
|
|
272
|
+
interface HcFetchResponse {
|
|
273
|
+
readonly ok: boolean;
|
|
274
|
+
readonly status: number;
|
|
275
|
+
readonly statusText: string;
|
|
276
|
+
readonly headers: HcFetchHeaders;
|
|
277
|
+
text(): Promise<string>;
|
|
278
|
+
json(): Promise<unknown>;
|
|
279
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
271
280
|
}
|
|
272
281
|
|
|
273
282
|
/**
|
|
@@ -468,12 +477,14 @@ interface HcScriptApi {
|
|
|
468
477
|
/** Read-only metadata about the current script run (Postman pm.info equivalent). */
|
|
469
478
|
info: HcInfoApi;
|
|
470
479
|
/**
|
|
471
|
-
* Sends an outbound HTTP request
|
|
480
|
+
* Sends an outbound HTTP request using the native fetch(input, init?) signature.
|
|
472
481
|
* Requires Settings → General → Allow script network requests.
|
|
473
482
|
*
|
|
474
|
-
* @
|
|
483
|
+
* @param input - URL string, URL, or Request-like `{ url }` object.
|
|
484
|
+
* @param init - Optional RequestInit-compatible options.
|
|
485
|
+
* @throws When the setting is disabled or fetch is unavailable in this context.
|
|
475
486
|
*/
|
|
476
|
-
|
|
487
|
+
fetch(input: string | URL | { url: string }, init?: HcFetchInit): Promise<HcFetchResponse>;
|
|
477
488
|
/**
|
|
478
489
|
* Sends a one-shot prompt to a configured AI model.
|
|
479
490
|
* Includes the current send's request (and response in post-request scripts)
|