dsh-ntr 0.1.6 → 0.2.0
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.en.md +1 -1
- package/README.md +1 -1
- package/cordis.patch.yml +18 -18
- package/lib/bridge.js +120 -291
- package/lib/client/acp-ui.js +60 -32
- package/lib/client/index.js +17 -0
- package/lib/client/panel.js +37 -42
- package/lib/client/permission-dock.js +72 -0
- package/lib/client/session-controls.js +4 -4
- package/lib/client/store.js +58 -12
- package/lib/client.js +296 -95
- package/lib/client.js.map +1 -1
- package/lib/impl.js +30 -19
- package/lib/index.js +6 -51
- package/lib/types/bridge.d.ts +60 -154
- package/lib/types/client/panel.d.ts +12 -19
- package/lib/types/client/permission-dock.d.ts +31 -0
- package/lib/types/client/store.d.ts +42 -12
- package/lib/types/impl.d.ts +31 -17
- package/lib/types/protocol.d.ts +27 -12
- package/package.json +1 -1
- package/src/bridge.ts +147 -357
- package/src/client/acp-ui.tsx +60 -32
- package/src/client/index.tsx +18 -0
- package/src/client/panel.tsx +54 -58
- package/src/client/permission-dock.tsx +133 -0
- package/src/client/session-controls.tsx +4 -4
- package/src/client/store.ts +81 -25
- package/src/impl.ts +49 -29
- package/src/index.ts +6 -65
- package/src/protocol.ts +32 -12
package/lib/client.js
CHANGED
|
@@ -14,6 +14,36 @@ window.__ModuleLoader__.load({
|
|
|
14
14
|
};
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/client/store.ts
|
|
17
|
+
/** Returned by {@link deadlineRemainingLabel} once the deadline has passed. */
|
|
18
|
+
const PERMISSION_DEADLINE_EXPIRED = "expired";
|
|
19
|
+
/**
|
|
20
|
+
* The asks that belong to one DSH chat conversation. An ask without a
|
|
21
|
+
* `dshSessionId` belongs to no conversation (an `/acp` RPC-only session), so it
|
|
22
|
+
* is deliberately absent from every conversation's dock and stays in the
|
|
23
|
+
* settings-panel ledger.
|
|
24
|
+
*/
|
|
25
|
+
function permissionsForSession(permissions, dshSessionId) {
|
|
26
|
+
return permissions.filter((permission) => permission.dshSessionId === dshSessionId);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Compact countdown for one ask, or `undefined` when there is nothing to count
|
|
30
|
+
* down (`wait` mode, or a frame without a deadline). Pure so the formatting is
|
|
31
|
+
* testable without a clock: the dock passes `Date.now()` on its tick.
|
|
32
|
+
*
|
|
33
|
+
* Past the deadline the answer is {@link PERMISSION_DEADLINE_EXPIRED} rather
|
|
34
|
+
* than a negative number — the host has already armed the expiry, and a card
|
|
35
|
+
* counting backwards would claim a deadline that no longer exists.
|
|
36
|
+
*/
|
|
37
|
+
function deadlineRemainingLabel(deadlineAt, now, mode) {
|
|
38
|
+
if (mode === "wait" || deadlineAt === void 0) return void 0;
|
|
39
|
+
const remainingMs = deadlineAt - now;
|
|
40
|
+
if (remainingMs <= 0) return PERMISSION_DEADLINE_EXPIRED;
|
|
41
|
+
const seconds = Math.ceil(remainingMs / 1e3);
|
|
42
|
+
if (seconds < 60) return `${String(seconds)}s`;
|
|
43
|
+
const minutes = Math.floor(seconds / 60);
|
|
44
|
+
const rest = seconds % 60;
|
|
45
|
+
return rest === 0 ? `${String(minutes)}m` : `${String(minutes)}m ${String(rest)}s`;
|
|
46
|
+
}
|
|
17
47
|
const MAX_BLOCKS = 400;
|
|
18
48
|
/** How many observed ACP transcripts stay live at once (insertion-ordered eviction). */
|
|
19
49
|
const MAX_WATCHED = 6;
|
|
@@ -284,7 +314,6 @@ window.__ModuleLoader__.load({
|
|
|
284
314
|
seatCatalogs: {},
|
|
285
315
|
probingModels: {},
|
|
286
316
|
probeErrors: {},
|
|
287
|
-
approvalRouted: false,
|
|
288
317
|
chatSessions: [],
|
|
289
318
|
tagHalo: { ...DEFAULT_TAG_HALO },
|
|
290
319
|
approvalDeadlines: void 0,
|
|
@@ -470,7 +499,7 @@ window.__ModuleLoader__.load({
|
|
|
470
499
|
/** Snapshot refresh used on panel open and after a stream outage. */
|
|
471
500
|
async resync() {
|
|
472
501
|
try {
|
|
473
|
-
const [{ agents
|
|
502
|
+
const [{ agents }, { sessions }, defaultAgentInfo, detectResult, presetResult, tagHaloResult, deadlinesResult] = await Promise.all([
|
|
474
503
|
this.call("agents/list"),
|
|
475
504
|
this.call("sessions/list"),
|
|
476
505
|
this.call("default/agent").catch(() => ({})),
|
|
@@ -486,7 +515,6 @@ window.__ModuleLoader__.load({
|
|
|
486
515
|
sessions,
|
|
487
516
|
defaultAgent: defaultAgentInfo?.agentId,
|
|
488
517
|
detectedAgents,
|
|
489
|
-
approvalRouted: approvalRouted === true,
|
|
490
518
|
preset: presetResult?.preset ?? void 0,
|
|
491
519
|
tagHalo: tagHaloResult?.halo ?? { ...DEFAULT_TAG_HALO },
|
|
492
520
|
approvalDeadlines: deadlinesResult.deadlines
|
|
@@ -1113,6 +1141,14 @@ window.__ModuleLoader__.load({
|
|
|
1113
1141
|
this.flashError(error);
|
|
1114
1142
|
}
|
|
1115
1143
|
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Answer one ask (`optionId` absent = cancel it). A failure is reported
|
|
1146
|
+
* through the store's notice — the settings panel renders that — AND
|
|
1147
|
+
* rethrown, so the conversation dock can pin the message next to the card
|
|
1148
|
+
* that failed instead of leaving it on a settings page the user may not
|
|
1149
|
+
* have open. Callers that cannot show it (the settings panel) swallow it
|
|
1150
|
+
* after the notice.
|
|
1151
|
+
*/
|
|
1116
1152
|
async respondPermission(requestId, optionId) {
|
|
1117
1153
|
try {
|
|
1118
1154
|
await this.call("permission/respond", {
|
|
@@ -1121,6 +1157,7 @@ window.__ModuleLoader__.load({
|
|
|
1121
1157
|
});
|
|
1122
1158
|
} catch (error) {
|
|
1123
1159
|
this.flashError(error);
|
|
1160
|
+
throw error;
|
|
1124
1161
|
}
|
|
1125
1162
|
}
|
|
1126
1163
|
async stopAgent(agentId) {
|
|
@@ -1437,13 +1474,17 @@ window.__ModuleLoader__.load({
|
|
|
1437
1474
|
return this.loadAgentCatalog(event.agentId);
|
|
1438
1475
|
}
|
|
1439
1476
|
case "permission/request": {
|
|
1440
|
-
const
|
|
1441
|
-
this.mutate({ permissions: [...this.state.permissions.filter((p) => p.requestId !== event.requestId), {
|
|
1477
|
+
const permission = {
|
|
1442
1478
|
requestId: event.requestId,
|
|
1443
1479
|
sessionId: event.sessionId,
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1480
|
+
...event.dshSessionId === void 0 ? {} : { dshSessionId: event.dshSessionId },
|
|
1481
|
+
toolName: event.toolName,
|
|
1482
|
+
...event.reason === void 0 ? {} : { reason: event.reason },
|
|
1483
|
+
options: event.options,
|
|
1484
|
+
timeoutMode: event.timeoutMode,
|
|
1485
|
+
...event.deadlineAt === void 0 ? {} : { deadlineAt: event.deadlineAt }
|
|
1486
|
+
};
|
|
1487
|
+
this.mutate({ permissions: [...this.state.permissions.filter((p) => p.requestId !== event.requestId), permission] });
|
|
1447
1488
|
return Promise.resolve();
|
|
1448
1489
|
}
|
|
1449
1490
|
case "permission/resolved":
|
|
@@ -2380,9 +2421,9 @@ window.__ModuleLoader__.load({
|
|
|
2380
2421
|
.ntr-acp-seat:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2381
2422
|
.ntr-acp-seat:disabled { opacity: 0.5; cursor: default; }
|
|
2382
2423
|
.ntr-acp-seat.is-open { border-color: var(--dsw-alias-state-business-primary, #2563eb); color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
2383
|
-
.ntr-acp-seat.is-detached { border-color: var(--
|
|
2424
|
+
.ntr-acp-seat.is-detached { border-color: var(--dsw-alias-state-warn-label, #d97706); color: var(--dsw-alias-state-warn-label, #d97706); }
|
|
2384
2425
|
.ntr-acp-seat-main { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2385
|
-
.ntr-acp-seat-sub { color: var(--dsw-alias-
|
|
2426
|
+
.ntr-acp-seat-sub { color: var(--dsw-alias-label-secondary, #6b7280); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2386
2427
|
.ntr-acp-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
|
|
2387
2428
|
.ntr-acp-tick { margin-left: auto; color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
2388
2429
|
.ntr-acp-check { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 13px; height: 13px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: 3px; font-size: 10px; line-height: 1; color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
@@ -2391,80 +2432,108 @@ window.__ModuleLoader__.load({
|
|
|
2391
2432
|
.ntr-acp-menu-head { padding: 8px 10px 6px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
|
|
2392
2433
|
.ntr-acp-menu-title { flex: 1; }
|
|
2393
2434
|
.ntr-acp-menu-spin { opacity: 0.5; }
|
|
2394
|
-
.ntr-acp-menu-note { padding: 0 10px 6px; font-size: 11px; color: var(--dsw-alias-
|
|
2395
|
-
.ntr-acp-menu-error { margin: 0 10px 6px; padding: 6px 8px; border-radius: 6px; background: #
|
|
2435
|
+
.ntr-acp-menu-note { padding: 0 10px 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); line-height: 1.5; }
|
|
2436
|
+
.ntr-acp-menu-error { margin: 0 10px 6px; padding: 6px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 11px; }
|
|
2396
2437
|
.ntr-acp-menu-body { overflow: auto; padding: 0 4px 6px; }
|
|
2397
2438
|
.ntr-acp-menu-section { display: flex; flex-direction: column; padding-top: 4px; }
|
|
2398
|
-
.ntr-acp-menu-section-title { padding: 6px 8px 4px; font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-
|
|
2439
|
+
.ntr-acp-menu-section-title { padding: 6px 8px 4px; font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2399
2440
|
.ntr-acp-menu-item { display: flex; align-items: center; gap: 6px; font: inherit; font-size: 12px; text-align: left; padding: 6px 8px; border: none; border-radius: 6px; background: none; color: inherit; cursor: pointer; width: 100%; }
|
|
2400
2441
|
.ntr-acp-menu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2401
2442
|
.ntr-acp-menu-item:disabled { opacity: 0.45; cursor: default; }
|
|
2402
2443
|
.ntr-acp-menu-item.is-active { font-weight: 600; }
|
|
2403
2444
|
.ntr-acp-menu-item-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2404
|
-
.ntr-acp-menu-item-detail { color: var(--dsw-alias-
|
|
2405
|
-
.ntr-acp-menu-empty { padding: 10px; text-align: center; color: var(--dsw-alias-
|
|
2445
|
+
.ntr-acp-menu-item-detail { color: var(--dsw-alias-label-secondary, #6b7280); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; text-align: right; }
|
|
2446
|
+
.ntr-acp-menu-empty { padding: 10px; text-align: center; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2406
2447
|
/* ---- ACP view tab ---- */
|
|
2407
2448
|
.ntr-acp-view { display: flex; flex-direction: column; height: 100%; min-height: 0; font-size: 13px; line-height: 1.6; }
|
|
2408
|
-
.ntr-acp-view-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2, #e5e7eb); font-size: 12px; color: var(--dsw-alias-
|
|
2449
|
+
.ntr-acp-view-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2, #e5e7eb); font-size: 12px; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2409
2450
|
.ntr-acp-view-agent { font-weight: 600; color: inherit; }
|
|
2410
2451
|
.ntr-acp-view-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; direction: rtl; text-align: left; }
|
|
2411
2452
|
.ntr-acp-view-head .ntr-acp-more { margin-left: auto; }
|
|
2412
2453
|
.ntr-acp-stream { flex: 1; min-height: 0; overflow: auto; padding: 12px 14px 24px; display: flex; flex-direction: column; gap: 10px; }
|
|
2413
2454
|
.ntr-acp-badge { font-size: 11px; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; }
|
|
2414
|
-
.ntr-acp-badge.is-done { color: #
|
|
2415
|
-
.ntr-acp-badge.is-busy { color: #1d4ed8; background: #dbeafe; }
|
|
2416
|
-
.ntr-acp-badge.is-warn { color: #b45309; background: #fef3c7; }
|
|
2417
|
-
.ntr-acp-badge.is-fail { color: #b91c1c; background: #
|
|
2418
|
-
.ntr-acp-badge.is-idle { color: var(--dsw-alias-
|
|
2455
|
+
.ntr-acp-badge.is-done { color: color-mix(in srgb, var(--dsw-alias-state-success-primary, #22c55e) 72%, var(--dsw-alias-label-primary, #111827)); background: var(--dsw-alias-state-success-tertiary, #dcfce7); }
|
|
2456
|
+
.ntr-acp-badge.is-busy { color: var(--dsw-alias-state-business-primary, #1d4ed8); background: var(--dsw-alias-state-business-tertiary, #dbeafe); }
|
|
2457
|
+
.ntr-acp-badge.is-warn { color: var(--dsw-alias-state-warn-label, #b45309); background: var(--dsw-alias-state-warn-tertiary, #fef3c7); }
|
|
2458
|
+
.ntr-acp-badge.is-fail { color: var(--dsw-alias-state-error-primary, #b91c1c); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); }
|
|
2459
|
+
.ntr-acp-badge.is-idle { color: var(--dsw-alias-label-secondary, #6b7280); background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
|
|
2419
2460
|
.ntr-acp-bubble { max-width: 88%; padding: 8px 12px; border-radius: 12px; white-space: pre-wrap; word-break: break-word; }
|
|
2420
2461
|
.ntr-acp-bubble.is-user { align-self: flex-end; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 12%, transparent); }
|
|
2421
2462
|
.ntr-acp-bubble.is-agent { align-self: flex-start; background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
|
|
2422
2463
|
.ntr-acp-inline-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 1px 4px; border-radius: 4px; background: color-mix(in srgb, currentColor 10%, transparent); }
|
|
2423
2464
|
.ntr-acp-code { margin: 6px 0; padding: 8px 10px; border-radius: 8px; background: #0f172a; color: #e2e8f0; overflow: auto; font-size: 12px; line-height: 1.5; }
|
|
2424
|
-
.ntr-acp-thought { border-left: 3px solid var(--dsw-alias-border-l2, #d1d5db); padding-left: 8px; color: var(--dsw-alias-
|
|
2465
|
+
.ntr-acp-thought { border-left: 3px solid var(--dsw-alias-border-l2, #d1d5db); padding-left: 8px; color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12px; }
|
|
2425
2466
|
.ntr-acp-thought > summary { cursor: pointer; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
2426
2467
|
.ntr-acp-thought-body { white-space: pre-wrap; padding: 4px 0 2px; }
|
|
2427
2468
|
.ntr-acp-tool { border: 1px solid var(--dsw-alias-border-l2, #e5e7eb); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }
|
|
2428
|
-
.ntr-acp-tool.is-fail { border-color: #fca5a5; }
|
|
2469
|
+
.ntr-acp-tool.is-fail { border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }
|
|
2429
2470
|
.ntr-acp-tool-head { display: flex; align-items: center; gap: 8px; padding: 7px 10px; font-size: 12px; }
|
|
2430
2471
|
.ntr-acp-tool-title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2431
2472
|
.ntr-acp-tool-body { margin: 0; padding: 8px 10px; border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); background: var(--dsw-alias-bg-layer-2, #f9fafb); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 340px; overflow: auto; }
|
|
2432
|
-
.ntr-acp-tool-input { background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-
|
|
2433
|
-
.ntr-acp-tool-meta { font-size: 11px; color: var(--dsw-alias-
|
|
2473
|
+
.ntr-acp-tool-input { background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #111827); max-height: 240px; }
|
|
2474
|
+
.ntr-acp-tool-meta { font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2434
2475
|
.ntr-acp-diff { border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); }
|
|
2435
2476
|
.ntr-acp-diff-head { display: flex; align-items: center; gap: 8px; padding: 5px 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; background: var(--dsw-alias-bg-layer-2, #f9fafb); }
|
|
2436
2477
|
.ntr-acp-diff-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2437
|
-
.ntr-acp-diff-stats .is-add { color: #15803d; }
|
|
2438
|
-
.ntr-acp-diff-stats .is-del { color: #b91c1c; }
|
|
2478
|
+
.ntr-acp-diff-stats .is-add { color: var(--dsw-alias-state-success-primary, #15803d); }
|
|
2479
|
+
.ntr-acp-diff-stats .is-del { color: var(--dsw-alias-state-error-primary, #b91c1c); }
|
|
2439
2480
|
.ntr-acp-diff-body { max-height: 360px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; }
|
|
2440
2481
|
.ntr-acp-diff-row { display: flex; gap: 8px; padding: 0 10px; white-space: pre; }
|
|
2441
|
-
.ntr-acp-diff-row.is-add { background: #dcfce7; }
|
|
2442
|
-
.ntr-acp-diff-row.is-del { background: #fee2e2; }
|
|
2443
|
-
.ntr-acp-diff-row.is-ctx { color: var(--dsw-alias-
|
|
2482
|
+
.ntr-acp-diff-row.is-add { background: var(--dsw-alias-file-diff-added-bg, #dcfce7); }
|
|
2483
|
+
.ntr-acp-diff-row.is-del { background: var(--dsw-alias-file-diff-deleted-bg, #fee2e2); }
|
|
2484
|
+
.ntr-acp-diff-row.is-ctx { color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2444
2485
|
.ntr-acp-diff-sign { width: 1ch; flex: none; opacity: 0.7; }
|
|
2445
2486
|
.ntr-acp-diff-text { overflow-x: auto; }
|
|
2446
|
-
.ntr-acp-diff-note { padding: 4px 10px 6px; font-size: 11px; color: var(--dsw-alias-
|
|
2487
|
+
.ntr-acp-diff-note { padding: 4px 10px 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2447
2488
|
.ntr-acp-more { font: inherit; font-size: 11.5px; padding: 3px 10px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }
|
|
2448
2489
|
.ntr-acp-more:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2449
2490
|
.ntr-acp-more:disabled { opacity: 0.5; cursor: default; }
|
|
2450
|
-
.ntr-acp-more.is-primary { border-color: transparent; background: var(--dsw-alias-button-primary-fill, #2563eb); color: var(--dsw-alias-
|
|
2491
|
+
.ntr-acp-more.is-primary { border-color: transparent; background: var(--dsw-alias-button-primary-fill, #2563eb); color: var(--dsw-alias-label-primary-foreground, #fff); }
|
|
2451
2492
|
.ntr-acp-tool > .ntr-acp-more, .ntr-acp-empty-actions .ntr-acp-more { margin: 6px 10px; }
|
|
2452
2493
|
.ntr-acp-plan { border: 1px solid var(--dsw-alias-border-l2, #e5e7eb); border-radius: 10px; padding: 8px 10px; background: var(--dsw-alias-bg-layer-1, #fff); }
|
|
2453
|
-
.ntr-acp-plan-head { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-
|
|
2494
|
+
.ntr-acp-plan-head { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); margin-bottom: 4px; }
|
|
2454
2495
|
.ntr-acp-plan-row { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; padding: 1px 0; }
|
|
2455
|
-
.ntr-acp-plan-row.is-completed { color: var(--dsw-alias-
|
|
2496
|
+
.ntr-acp-plan-row.is-completed { color: var(--dsw-alias-label-secondary, #6b7280); text-decoration: line-through; }
|
|
2456
2497
|
.ntr-acp-plan-row.is-in_progress { font-weight: 600; }
|
|
2457
2498
|
.ntr-acp-plan-glyph { width: 1ch; flex: none; }
|
|
2458
|
-
.ntr-acp-plan-prio { margin-left: auto; font-size: 10px; text-transform: uppercase; color: var(--dsw-alias-
|
|
2499
|
+
.ntr-acp-plan-prio { margin-left: auto; font-size: 10px; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2459
2500
|
.ntr-acp-notice { padding: 6px 10px; border-radius: 8px; font-size: 12px; }
|
|
2460
|
-
.ntr-acp-notice.is-error { background: #
|
|
2461
|
-
.ntr-acp-notice.is-info { background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--dsw-alias-
|
|
2501
|
+
.ntr-acp-notice.is-error { background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); }
|
|
2502
|
+
.ntr-acp-notice.is-info { background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2462
2503
|
.ntr-acp-empty { margin: auto; max-width: 460px; text-align: center; display: flex; flex-direction: column; gap: 10px; padding: 24px; }
|
|
2463
2504
|
.ntr-acp-empty-title { font-size: 14px; font-weight: 600; }
|
|
2464
|
-
.ntr-acp-empty-body { margin: 0; color: var(--dsw-alias-
|
|
2465
|
-
.ntr-acp-empty-hint { color: var(--dsw-alias-
|
|
2466
|
-
.ntr-acp-seat-diag { font-family: var(--
|
|
2505
|
+
.ntr-acp-empty-body { margin: 0; color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12.5px; line-height: 1.7; }
|
|
2506
|
+
.ntr-acp-empty-hint { color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12px; }
|
|
2507
|
+
.ntr-acp-seat-diag { font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace); opacity: 0.72; word-break: break-word; }
|
|
2467
2508
|
.ntr-acp-empty-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
|
|
2509
|
+
/* ---- Pending permission asks (conversation.input.dock) ---- */
|
|
2510
|
+
/* The dock slot is the full conversation column, but the composer card inside
|
|
2511
|
+
it is narrower (InputBar: side clearance + card max width). Mirror that
|
|
2512
|
+
formula so each ask lines up with the input box instead of spanning the
|
|
2513
|
+
column. The list owns a bounded height and scrolls so a burst of asks cannot
|
|
2514
|
+
push the composer off screen. */
|
|
2515
|
+
.ntr-permission-dock {
|
|
2516
|
+
box-sizing: border-box;
|
|
2517
|
+
width: calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px));
|
|
2518
|
+
max-width: var(--dsh-composer-card-max-width, 920px);
|
|
2519
|
+
margin: 0 auto;
|
|
2520
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
2521
|
+
max-height: min(38vh, 320px); overflow-y: auto;
|
|
2522
|
+
font-size: 12px; line-height: 1.5;
|
|
2523
|
+
}
|
|
2524
|
+
.ntr-permission-card { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); }
|
|
2525
|
+
.ntr-permission-head { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
|
|
2526
|
+
.ntr-permission-tool { flex: none; font-weight: 600; }
|
|
2527
|
+
.ntr-permission-reason { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2528
|
+
.ntr-permission-deadline { flex: none; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); font-variant-numeric: tabular-nums; }
|
|
2529
|
+
.ntr-permission-deadline.is-expired { color: var(--dsw-alias-state-warn-label, #b45309); font-weight: 600; }
|
|
2530
|
+
.ntr-permission-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
|
2531
|
+
.ntr-permission-option, .ntr-permission-cancel { font: inherit; font-size: 12px; line-height: 1.4; padding: 4px 10px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; white-space: normal; text-align: left; }
|
|
2532
|
+
.ntr-permission-option:hover:not(:disabled), .ntr-permission-cancel:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2533
|
+
.ntr-permission-option:disabled, .ntr-permission-cancel:disabled { opacity: 0.5; cursor: default; }
|
|
2534
|
+
.ntr-permission-option:focus-visible, .ntr-permission-cancel:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #2563eb); outline-offset: 2px; }
|
|
2535
|
+
.ntr-permission-cancel { margin-left: auto; color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
2536
|
+
.ntr-permission-error { padding: 4px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 11px; word-break: break-word; }
|
|
2468
2537
|
/* ---- NTR settings dialog (shell.overlay) ---- */
|
|
2469
2538
|
/* Frame-wide surface opened from the agent base's empty-roster menu. The
|
|
2470
2539
|
overlay layer itself is click-through, so the layer opts back in and blocks
|
|
@@ -2501,11 +2570,11 @@ window.__ModuleLoader__.load({
|
|
|
2501
2570
|
* React surface for ACP: the settings configuration section.
|
|
2502
2571
|
*
|
|
2503
2572
|
* Scope is deliberately narrow — this is a CONFIGURATION surface. Runtime
|
|
2504
|
-
* interaction lives in the conversation: permission
|
|
2505
|
-
*
|
|
2506
|
-
*
|
|
2507
|
-
*
|
|
2508
|
-
*
|
|
2573
|
+
* interaction lives in the conversation: every permission ask with a chat
|
|
2574
|
+
* identity is answered on a card above that conversation's composer
|
|
2575
|
+
* (`permission-dock.tsx`). The ledger card here is the cross-session mirror —
|
|
2576
|
+
* asks whose conversation is not open, or which have no chat identity at all —
|
|
2577
|
+
* and is where those are answered (see `permissionLedgerCard`).
|
|
2509
2578
|
*
|
|
2510
2579
|
* Each configured agent is one compact row (status + current model + badges)
|
|
2511
2580
|
* that expands into the full connection form and model catalog. The row stays
|
|
@@ -2537,8 +2606,10 @@ window.__ModuleLoader__.load({
|
|
|
2537
2606
|
--ntr-radius: 10px; --ntr-radius-sm: 7px;
|
|
2538
2607
|
--ntr-font: 13px; --ntr-font-sm: 12px; --ntr-font-xs: 11px;
|
|
2539
2608
|
--ntr-line: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
|
|
2540
|
-
--ntr-muted: var(--dsw-alias-
|
|
2541
|
-
--ntr-danger:
|
|
2609
|
+
--ntr-muted: var(--dsw-alias-label-secondary, #6b7280);
|
|
2610
|
+
--ntr-danger: var(--dsw-alias-state-error-primary, #dc2626);
|
|
2611
|
+
--ntr-warn: var(--dsw-alias-state-warn-label, #b45309);
|
|
2612
|
+
--ntr-success: color-mix(in srgb, var(--dsw-alias-state-success-primary, #16a34a) 72%, var(--dsw-alias-label-primary, #111827));
|
|
2542
2613
|
--ntr-accent: var(--dsw-alias-button-primary-fill, #2563eb);
|
|
2543
2614
|
width: 100%; max-width: 960px; box-sizing: border-box; margin: 0 auto;
|
|
2544
2615
|
padding: 20px; overflow: auto; font-size: var(--ntr-font); line-height: 1.55;
|
|
@@ -2562,25 +2633,25 @@ window.__ModuleLoader__.load({
|
|
|
2562
2633
|
}
|
|
2563
2634
|
.dwa-btn:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); border-color: var(--dsw-alias-border-l1, #9ca3af); }
|
|
2564
2635
|
.dwa-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
2565
|
-
.dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-
|
|
2636
|
+
.dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); border-color: transparent; font-weight: 550; }
|
|
2566
2637
|
.dwa-btn-primary:hover { background: var(--ntr-accent); border-color: transparent; opacity: 0.88; }
|
|
2567
2638
|
.dwa-btn-secondary { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
|
|
2568
|
-
.dwa-btn-danger { color: var(--ntr-danger); border-color: #fca5a5; }
|
|
2569
|
-
.dwa-btn-danger:hover { background: #fee2e2; }
|
|
2639
|
+
.dwa-btn-danger { color: var(--ntr-danger); border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }
|
|
2640
|
+
.dwa-btn-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, #fee2e2); }
|
|
2570
2641
|
.dwa-section { margin-bottom: 24px; }
|
|
2571
2642
|
.dwa-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
|
|
2572
2643
|
.dwa-section-title { font-size: 14px; font-weight: 650; margin: 0; display: flex; align-items: center; gap: 8px; }
|
|
2573
2644
|
.dwa-section-copy { margin: 3px 0 0; font-size: var(--ntr-font-xs); color: var(--ntr-muted); }
|
|
2574
2645
|
.dwa-card { border: var(--ntr-line); border-radius: var(--ntr-radius); padding: 16px; background: var(--dsw-alias-bg-layer-1, #fff); font-size: var(--ntr-font); line-height: 1.55; }
|
|
2575
|
-
.dwa-card-warn { border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-
|
|
2646
|
+
.dwa-card-warn { border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-tertiary, #fffbeb); }
|
|
2576
2647
|
.dwa-mode-banner { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 16px; align-items: start; margin-bottom: 24px; padding: 14px 16px; }
|
|
2577
2648
|
.dwa-mode-title { display: flex; align-items: center; gap: 8px; font-weight: 650; }
|
|
2578
2649
|
.dwa-mode-copy { grid-column: 1 / -1; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
2579
2650
|
.dwa-mode-source { display: block; margin-top: 5px; overflow-wrap: anywhere; opacity: 0.85; }
|
|
2580
2651
|
.dwa-badge { font-size: var(--ntr-font-xs); line-height: 1.4; border-radius: 999px; padding: 2px 8px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font-weight: 550; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
|
|
2581
|
-
.dwa-badge-success { background: #
|
|
2582
|
-
.dwa-badge-warn { background: #
|
|
2583
|
-
.dwa-badge-info { background: #
|
|
2652
|
+
.dwa-badge-success { background: var(--dsw-alias-state-success-tertiary, #dcfce7); color: var(--ntr-success); }
|
|
2653
|
+
.dwa-badge-warn { background: var(--dsw-alias-state-warn-tertiary, #fef3c7); color: var(--ntr-warn); }
|
|
2654
|
+
.dwa-badge-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
2584
2655
|
.dwa-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
|
|
2585
2656
|
.dwa-agent-list { display: flex; flex-direction: column; gap: 8px; }
|
|
2586
2657
|
.dwa-agent-card { border: var(--ntr-line); border-radius: var(--ntr-radius); background: var(--dsw-alias-bg-layer-1, #fff); overflow: visible; transition: border-color 0.15s ease; }
|
|
@@ -2602,7 +2673,7 @@ window.__ModuleLoader__.load({
|
|
|
2602
2673
|
.dwa-subsection-copy { margin: 3px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
2603
2674
|
.dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: flex-start; }
|
|
2604
2675
|
.dwa-form-col { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
2605
|
-
.dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-
|
|
2676
|
+
.dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-label-primary, #374151); }
|
|
2606
2677
|
.dwa-hint { font-size: var(--ntr-font-xs); line-height: 1.55; color: var(--ntr-muted); }
|
|
2607
2678
|
.dwa-error { font-size: var(--ntr-font-xs); color: var(--ntr-danger); }
|
|
2608
2679
|
.dwa-input { min-height: 36px; font: inherit; font-size: var(--ntr-font-sm); padding: 7px 10px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; width: 100%; box-sizing: border-box; }
|
|
@@ -2627,7 +2698,7 @@ window.__ModuleLoader__.load({
|
|
|
2627
2698
|
.dwa-add-content { border-top: var(--ntr-line); padding: 14px 16px 16px; }
|
|
2628
2699
|
.dwa-chip { min-height: 30px; font: inherit; font-size: var(--ntr-font-sm); border: var(--ntr-line); border-radius: 999px; padding: 4px 12px; background: var(--dsw-alias-bg-layer-1, #fff); cursor: pointer; color: inherit; }
|
|
2629
2700
|
.dwa-chip:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
2630
|
-
.dwa-chip-active { border-color: var(--ntr-accent); background: #
|
|
2701
|
+
.dwa-chip-active { border-color: var(--ntr-accent); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); font-weight: 550; }
|
|
2631
2702
|
.dwa-chip-list { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }
|
|
2632
2703
|
.dwa-switch { display: inline-flex; align-items: center; gap: 7px; font-size: var(--ntr-font-sm); cursor: pointer; user-select: none; }
|
|
2633
2704
|
.dwa-inline-setting { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-2, #f9fafb); }
|
|
@@ -2640,7 +2711,7 @@ window.__ModuleLoader__.load({
|
|
|
2640
2711
|
/* Tall enough for a comfortable selection (about ten rows) before scrolling;
|
|
2641
2712
|
the cap keeps the two stacked panes from pushing the rest of the card away. */
|
|
2642
2713
|
.dwa-model-pane { min-width: 0; max-height: 360px; display: flex; flex-direction: column; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }
|
|
2643
|
-
.dwa-model-pane-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; border-bottom: var(--ntr-line); font-size: var(--ntr-font-xs); font-weight: 600; color: var(--dsw-alias-
|
|
2714
|
+
.dwa-model-pane-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; border-bottom: var(--ntr-line); font-size: var(--ntr-font-xs); font-weight: 600; color: var(--dsw-alias-label-secondary, #4b5563); }
|
|
2644
2715
|
.dwa-model-pane-head .dwa-btn { margin-left: auto; min-height: 26px; padding: 2px 8px; font-size: var(--ntr-font-xs); }
|
|
2645
2716
|
.dwa-model-pane-head .dwa-caret-btn { flex: none; width: 22px; height: 22px; background: transparent; }
|
|
2646
2717
|
.dwa-model-pane-head .dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); }
|
|
@@ -2658,7 +2729,7 @@ window.__ModuleLoader__.load({
|
|
|
2658
2729
|
.dwa-model-empty { padding: 6px; }
|
|
2659
2730
|
.dwa-model-row { display: flex; align-items: center; gap: 6px; width: 100%; border: none; background: none; padding: 5px 6px; border-radius: 5px; cursor: pointer; color: inherit; font: inherit; font-size: var(--ntr-font-sm); text-align: left; }
|
|
2660
2731
|
.dwa-model-row:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
2661
|
-
.dwa-model-row.is-on { background: #
|
|
2732
|
+
.dwa-model-row.is-on { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
2662
2733
|
.dwa-model-row-mark { width: 12px; flex: none; text-align: center; opacity: 0.75; }
|
|
2663
2734
|
.dwa-model-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2664
2735
|
/* Name and credit keep their room; the id is the only cell that collapses. */
|
|
@@ -2680,7 +2751,7 @@ window.__ModuleLoader__.load({
|
|
|
2680
2751
|
.dwa-seg > button { min-height: 30px; font: inherit; font-size: var(--ntr-font-sm); color: inherit; border: none; background: var(--dsw-alias-bg-layer-1, #fff); padding: 4px 14px; cursor: pointer; }
|
|
2681
2752
|
.dwa-seg > button + button { border-left: 1px solid var(--dsw-alias-border-l2, #d1d5db); }
|
|
2682
2753
|
.dwa-seg > button:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
2683
|
-
.dwa-seg > button.is-on { background: var(--ntr-accent); color: var(--dsw-alias-
|
|
2754
|
+
.dwa-seg > button.is-on { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); font-weight: 550; }
|
|
2684
2755
|
.dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; }
|
|
2685
2756
|
.dwa-preference-row + .dwa-preference-row { border-top: var(--ntr-line); }
|
|
2686
2757
|
/* A control that needs the whole card: the label sits above it, not beside
|
|
@@ -2739,19 +2810,19 @@ window.__ModuleLoader__.load({
|
|
|
2739
2810
|
function stateVisual(state, acp) {
|
|
2740
2811
|
switch (state) {
|
|
2741
2812
|
case "ready": return {
|
|
2742
|
-
color: "#22c55e",
|
|
2813
|
+
color: "var(--dsw-alias-state-success-primary, #22c55e)",
|
|
2743
2814
|
text: acp.label("已连接", "Connected")
|
|
2744
2815
|
};
|
|
2745
2816
|
case "starting": return {
|
|
2746
|
-
color: "#eab308",
|
|
2817
|
+
color: "var(--dsw-alias-state-warn-primary, #eab308)",
|
|
2747
2818
|
text: acp.label("启动中", "Starting")
|
|
2748
2819
|
};
|
|
2749
2820
|
case "error": return {
|
|
2750
|
-
color: "#ef4444",
|
|
2821
|
+
color: "var(--dsw-alias-state-error-primary, #ef4444)",
|
|
2751
2822
|
text: acp.label("错误", "Error")
|
|
2752
2823
|
};
|
|
2753
2824
|
default: return {
|
|
2754
|
-
color: "#9ca3af",
|
|
2825
|
+
color: "var(--dsw-alias-state-idle-primary, #9ca3af)",
|
|
2755
2826
|
text: acp.label("已配置", "Configured"),
|
|
2756
2827
|
title: acp.label("已配置,进程不常驻。在 DSH 聊天中首次使用时自动连接;展开区可用「测试连接」验证命令能否正常起来。", "Configured; the process is not kept resident. It connects automatically the first time a DSH chat turn uses it — use “Test connection” in the expanded panel to verify the command starts.")
|
|
2757
2828
|
};
|
|
@@ -2924,25 +2995,15 @@ window.__ModuleLoader__.load({
|
|
|
2924
2995
|
/**
|
|
2925
2996
|
* The settings panel's permission-ledger rule.
|
|
2926
2997
|
*
|
|
2927
|
-
*
|
|
2928
|
-
*
|
|
2929
|
-
*
|
|
2930
|
-
*
|
|
2931
|
-
*
|
|
2932
|
-
*
|
|
2933
|
-
* (`approvalRouted` → a read-only count, no buttons) hid the only answer
|
|
2934
|
-
* entry exactly when the native route had degraded to unavailable, and the
|
|
2935
|
-
* ask then hung until its timeout.
|
|
2936
|
-
*
|
|
2937
|
-
* `fellBackFromConversation` is the case the card has to explain: the host
|
|
2938
|
-
* can route asks, and this one still did not make it.
|
|
2998
|
+
* Pending asks are shown in the conversation card while that conversation is
|
|
2999
|
+
* open; this card is the cross-session mirror for the ones it cannot show —
|
|
3000
|
+
* asks belonging to another conversation, and `/acp` RPC-only asks with no
|
|
3001
|
+
* chat identity at all. It therefore appears whenever ANY ask is pending: a
|
|
3002
|
+
* pending entry with no conversation card would otherwise have no answer
|
|
3003
|
+
* surface anywhere.
|
|
2939
3004
|
*/
|
|
2940
3005
|
function permissionLedgerCard(ui) {
|
|
2941
|
-
|
|
2942
|
-
return {
|
|
2943
|
-
visible,
|
|
2944
|
-
fellBackFromConversation: visible && ui.approvalRouted
|
|
2945
|
-
};
|
|
3006
|
+
return { visible: ui.permissions.length > 0 };
|
|
2946
3007
|
}
|
|
2947
3008
|
/**
|
|
2948
3009
|
* The ONE wait window, as the rungs the slider snaps to.
|
|
@@ -3272,7 +3333,7 @@ window.__ModuleLoader__.load({
|
|
|
3272
3333
|
children: [
|
|
3273
3334
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3274
3335
|
className: "dwa-dot",
|
|
3275
|
-
style: { background: ui.stream === "live" ? "#22c55e" : ui.stream === "down" ? "#ef4444" : "#9ca3af" },
|
|
3336
|
+
style: { background: ui.stream === "live" ? "var(--dsw-alias-state-success-primary, #22c55e)" : ui.stream === "down" ? "var(--dsw-alias-state-error-primary, #ef4444)" : "var(--dsw-alias-state-idle-primary, #9ca3af)" },
|
|
3276
3337
|
title: ui.stream
|
|
3277
3338
|
}),
|
|
3278
3339
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: streamText(ui.stream, acp) }),
|
|
@@ -3300,14 +3361,14 @@ window.__ModuleLoader__.load({
|
|
|
3300
3361
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3301
3362
|
style: {
|
|
3302
3363
|
fontWeight: 600,
|
|
3303
|
-
marginBottom:
|
|
3364
|
+
marginBottom: 4
|
|
3304
3365
|
},
|
|
3305
3366
|
children: acp.label("待处理权限请求", "Pending Permission Requests")
|
|
3306
3367
|
}),
|
|
3307
|
-
|
|
3368
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3308
3369
|
className: "dwa-hint",
|
|
3309
3370
|
style: { marginBottom: 8 },
|
|
3310
|
-
children: acp.label("
|
|
3371
|
+
children: acp.label("对话打开时在那边的卡片上回答;这里兜底显示其他会话的询问。", "Answer in the conversation card while that conversation is open; this list covers the asks whose conversation is not.")
|
|
3311
3372
|
}),
|
|
3312
3373
|
ui.permissions.map((permission) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3313
3374
|
style: {
|
|
@@ -3318,21 +3379,31 @@ window.__ModuleLoader__.load({
|
|
|
3318
3379
|
flexWrap: "wrap"
|
|
3319
3380
|
},
|
|
3320
3381
|
children: [
|
|
3321
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3382
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3322
3383
|
style: {
|
|
3323
3384
|
flex: 1,
|
|
3324
3385
|
minWidth: 160
|
|
3325
3386
|
},
|
|
3326
|
-
children:
|
|
3387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3388
|
+
style: { fontWeight: 600 },
|
|
3389
|
+
children: permission.toolName
|
|
3390
|
+
}), permission.reason !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3391
|
+
style: { color: "var(--ntr-muted)" },
|
|
3392
|
+
children: [" · ", permission.reason]
|
|
3393
|
+
})]
|
|
3327
3394
|
}),
|
|
3328
3395
|
permission.options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3329
3396
|
className: "dwa-btn dwa-btn-primary",
|
|
3330
|
-
onClick: () =>
|
|
3397
|
+
onClick: () => {
|
|
3398
|
+
acp.respondPermission(permission.requestId, option.optionId).catch(() => void 0);
|
|
3399
|
+
},
|
|
3331
3400
|
children: option.name
|
|
3332
3401
|
}, option.optionId)),
|
|
3333
3402
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3334
3403
|
className: "dwa-btn",
|
|
3335
|
-
onClick: () =>
|
|
3404
|
+
onClick: () => {
|
|
3405
|
+
acp.respondPermission(permission.requestId).catch(() => void 0);
|
|
3406
|
+
},
|
|
3336
3407
|
children: acp.label("拒绝", "Dismiss")
|
|
3337
3408
|
})
|
|
3338
3409
|
]
|
|
@@ -4022,7 +4093,7 @@ window.__ModuleLoader__.load({
|
|
|
4022
4093
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("code", {
|
|
4023
4094
|
className: "dwa-input-mono",
|
|
4024
4095
|
style: {
|
|
4025
|
-
background: "#0000000a",
|
|
4096
|
+
background: "var(--dsw-alias-markdown-inline-code, #0000000a)",
|
|
4026
4097
|
padding: "2px 6px",
|
|
4027
4098
|
borderRadius: 4
|
|
4028
4099
|
},
|
|
@@ -4221,7 +4292,7 @@ window.__ModuleLoader__.load({
|
|
|
4221
4292
|
fontSize: 12,
|
|
4222
4293
|
whiteSpace: "pre-wrap",
|
|
4223
4294
|
wordBreak: "break-all",
|
|
4224
|
-
color: testResult.ok ? "
|
|
4295
|
+
color: testResult.ok ? "var(--ntr-success)" : "var(--ntr-danger)"
|
|
4225
4296
|
},
|
|
4226
4297
|
children: [testResult.ok ? "✓ " : "✕ ", testResult.text]
|
|
4227
4298
|
})
|
|
@@ -4610,7 +4681,7 @@ window.__ModuleLoader__.load({
|
|
|
4610
4681
|
onChange: (e) => void toggleAutoDiscover(e.target.checked),
|
|
4611
4682
|
style: {
|
|
4612
4683
|
margin: 0,
|
|
4613
|
-
accentColor: "#2563eb"
|
|
4684
|
+
accentColor: "var(--dsw-alias-state-business-primary, #2563eb)"
|
|
4614
4685
|
}
|
|
4615
4686
|
}), acp.label("自动发现新模型", "Auto-discover new models")]
|
|
4616
4687
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -5088,10 +5159,10 @@ window.__ModuleLoader__.load({
|
|
|
5088
5159
|
* the entry point for conversations no agent has served.
|
|
5089
5160
|
*/
|
|
5090
5161
|
const STATE_COLORS = {
|
|
5091
|
-
ready: "#22c55e",
|
|
5092
|
-
starting: "#eab308",
|
|
5093
|
-
error: "#ef4444",
|
|
5094
|
-
stopped: "#9ca3af"
|
|
5162
|
+
ready: "var(--dsw-alias-state-success-primary, #22c55e)",
|
|
5163
|
+
starting: "var(--dsw-alias-state-warn-primary, #eab308)",
|
|
5164
|
+
error: "var(--dsw-alias-state-error-primary, #ef4444)",
|
|
5165
|
+
stopped: "var(--dsw-alias-state-idle-primary, #9ca3af)"
|
|
5095
5166
|
};
|
|
5096
5167
|
function agentStateText(acp, state) {
|
|
5097
5168
|
switch (state) {
|
|
@@ -6465,6 +6536,130 @@ window.__ModuleLoader__.load({
|
|
|
6465
6536
|
});
|
|
6466
6537
|
}
|
|
6467
6538
|
//#endregion
|
|
6539
|
+
//#region src/client/permission-dock.tsx
|
|
6540
|
+
/**
|
|
6541
|
+
* Pending ACP permission asks for the staged DSH conversation, rendered as
|
|
6542
|
+
* composer-width entries above the composer card (`conversation.input.dock`).
|
|
6543
|
+
* The slot itself spans the conversation column; the dock mirrors the host's
|
|
6544
|
+
* InputBar width formula so the cards line up with the input box.
|
|
6545
|
+
*
|
|
6546
|
+
* The host broadcasts every ask as `permission/request`, carrying the DSH chat
|
|
6547
|
+
* session id when the ask belongs to a conversation. This dock is the primary
|
|
6548
|
+
* answer surface: it renders exactly the asks whose `dshSessionId` is the
|
|
6549
|
+
* conversation it was mounted for, and nothing otherwise. There is no
|
|
6550
|
+
* ACP-mode gate — the frame set itself is the gate, so an ordinary
|
|
6551
|
+
* conversation shows this card if and only if it actually has an ask.
|
|
6552
|
+
*
|
|
6553
|
+
* The ask's description and options are the agent's own: buttons appear in the
|
|
6554
|
+
* agent's order with the agent's names (including "Always Allow"), and
|
|
6555
|
+
* `kind` is not rendered. A separate cancel button answers with no option,
|
|
6556
|
+
* which the host forwards as `cancelled`. No Enter/Escape handlers: the
|
|
6557
|
+
* composer owns its keyboard, and these are ordinary focusable buttons.
|
|
6558
|
+
*
|
|
6559
|
+
* Multiple asks stack in ARRIVAL order, oldest first: the question ignored
|
|
6560
|
+
* longest stays at the top. The list is capped in height by CSS and scrolls.
|
|
6561
|
+
* Asks without a chat identity never appear here — they stay in the
|
|
6562
|
+
* settings-panel ledger, the cross-session mirror.
|
|
6563
|
+
*/
|
|
6564
|
+
/** Tick interval for the countdown (seconds are all the label shows). */
|
|
6565
|
+
const COUNTDOWN_TICK_MS = 1e3;
|
|
6566
|
+
function PermissionDock({ acp, sessionId }) {
|
|
6567
|
+
ensureAcpStyle();
|
|
6568
|
+
const ui = useUi$2(acp);
|
|
6569
|
+
const dshSessionId = String(sessionId);
|
|
6570
|
+
const pending = permissionsForSession(ui.permissions, dshSessionId);
|
|
6571
|
+
const counting = pending.some((ask) => ask.timeoutMode !== "wait" && ask.deadlineAt !== void 0);
|
|
6572
|
+
const [now, setNow] = (0, react.useState)(() => Date.now());
|
|
6573
|
+
(0, react.useEffect)(() => {
|
|
6574
|
+
if (!counting) return;
|
|
6575
|
+
setNow(Date.now());
|
|
6576
|
+
const timer = setInterval(() => {
|
|
6577
|
+
setNow(Date.now());
|
|
6578
|
+
}, COUNTDOWN_TICK_MS);
|
|
6579
|
+
return () => {
|
|
6580
|
+
clearInterval(timer);
|
|
6581
|
+
};
|
|
6582
|
+
}, [counting]);
|
|
6583
|
+
if (pending.length === 0) return null;
|
|
6584
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6585
|
+
className: "ntr-permission-dock",
|
|
6586
|
+
role: "region",
|
|
6587
|
+
"aria-label": acp.label("待处理的权限请求", "Pending permission requests"),
|
|
6588
|
+
children: pending.map((permission) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PermissionAskCard, {
|
|
6589
|
+
acp,
|
|
6590
|
+
permission,
|
|
6591
|
+
now
|
|
6592
|
+
}, permission.requestId))
|
|
6593
|
+
});
|
|
6594
|
+
}
|
|
6595
|
+
function PermissionAskCard(props) {
|
|
6596
|
+
const { acp, permission, now } = props;
|
|
6597
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
6598
|
+
const [error, setError] = (0, react.useState)();
|
|
6599
|
+
const remaining = deadlineRemainingLabel(permission.deadlineAt, now, permission.timeoutMode);
|
|
6600
|
+
const expired = remaining === PERMISSION_DEADLINE_EXPIRED;
|
|
6601
|
+
const respond = (optionId) => {
|
|
6602
|
+
if (busy) return;
|
|
6603
|
+
setBusy(true);
|
|
6604
|
+
setError(void 0);
|
|
6605
|
+
acp.respondPermission(permission.requestId, optionId).catch((failure) => {
|
|
6606
|
+
setError(failure instanceof Error ? failure.message : String(failure));
|
|
6607
|
+
}).finally(() => {
|
|
6608
|
+
setBusy(false);
|
|
6609
|
+
});
|
|
6610
|
+
};
|
|
6611
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6612
|
+
className: "ntr-permission-card",
|
|
6613
|
+
children: [
|
|
6614
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6615
|
+
className: "ntr-permission-head",
|
|
6616
|
+
children: [
|
|
6617
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6618
|
+
className: "ntr-permission-tool",
|
|
6619
|
+
children: permission.toolName
|
|
6620
|
+
}),
|
|
6621
|
+
permission.reason !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6622
|
+
className: "ntr-permission-reason",
|
|
6623
|
+
title: permission.reason,
|
|
6624
|
+
children: permission.reason
|
|
6625
|
+
}),
|
|
6626
|
+
remaining !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6627
|
+
className: `ntr-permission-deadline${expired ? " is-expired" : ""}`,
|
|
6628
|
+
children: expired ? acp.label("已超时", "expired") : acp.label(`剩余 ${remaining}`, `${remaining} left`)
|
|
6629
|
+
})
|
|
6630
|
+
]
|
|
6631
|
+
}),
|
|
6632
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6633
|
+
className: "ntr-permission-actions",
|
|
6634
|
+
children: [permission.options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6635
|
+
type: "button",
|
|
6636
|
+
className: "ntr-permission-option",
|
|
6637
|
+
disabled: busy,
|
|
6638
|
+
title: option.name,
|
|
6639
|
+
onClick: () => {
|
|
6640
|
+
respond(option.optionId);
|
|
6641
|
+
},
|
|
6642
|
+
children: option.name
|
|
6643
|
+
}, option.optionId)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6644
|
+
type: "button",
|
|
6645
|
+
className: "ntr-permission-cancel",
|
|
6646
|
+
disabled: busy,
|
|
6647
|
+
title: acp.label("取消这次询问(不选择任何选项)", "Cancel this ask without choosing an option"),
|
|
6648
|
+
onClick: () => {
|
|
6649
|
+
respond();
|
|
6650
|
+
},
|
|
6651
|
+
children: acp.label("取消", "Cancel")
|
|
6652
|
+
})]
|
|
6653
|
+
}),
|
|
6654
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6655
|
+
className: "ntr-permission-error",
|
|
6656
|
+
role: "alert",
|
|
6657
|
+
children: error
|
|
6658
|
+
})
|
|
6659
|
+
]
|
|
6660
|
+
});
|
|
6661
|
+
}
|
|
6662
|
+
//#endregion
|
|
6468
6663
|
//#region src/client/model-seat.tsx
|
|
6469
6664
|
/** Exact key of the composer's model seat (a `single`, session-scoped slot). */
|
|
6470
6665
|
const MODEL_SEAT_KEY = "conversation.input.model";
|
|
@@ -7125,6 +7320,12 @@ window.__ModuleLoader__.load({
|
|
|
7125
7320
|
order: 5,
|
|
7126
7321
|
inject: (sessionId) => chatFace(sessionId)
|
|
7127
7322
|
}, AcpComposerAgentTag)), "dsh-ntr: composer agent tag");
|
|
7323
|
+
ctx.effect(() => ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
7324
|
+
name: "conversation.input.dock",
|
|
7325
|
+
id: "acp-permissions",
|
|
7326
|
+
order: 10,
|
|
7327
|
+
inject: (sessionId) => chatFace(sessionId)
|
|
7328
|
+
}, PermissionDock)), "dsh-ntr: conversation permission dock");
|
|
7128
7329
|
/**
|
|
7129
7330
|
* The client runtime's session list store, read structurally. Used to find
|
|
7130
7331
|
* the session the composer is staged on and whether it runs the ACP preset —
|