dsh-ntr 0.1.5 → 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 +4 -2
- package/README.md +4 -2
- package/cordis.patch.yml +56 -28
- package/lib/bridge.js +378 -330
- package/lib/client/acp-ui.js +60 -32
- package/lib/client/index.js +17 -0
- package/lib/client/panel.js +257 -103
- package/lib/client/permission-dock.js +72 -0
- package/lib/client/session-controls.js +4 -4
- package/lib/client/store.js +88 -22
- package/lib/client.js +584 -226
- package/lib/client.js.map +1 -1
- package/lib/impl.js +128 -45
- package/lib/index.js +20 -52
- package/lib/types/bridge.d.ts +164 -214
- package/lib/types/client/panel.d.ts +59 -20
- package/lib/types/client/permission-dock.d.ts +31 -0
- package/lib/types/client/store.d.ts +52 -15
- package/lib/types/impl.d.ts +93 -27
- package/lib/types/protocol.d.ts +137 -52
- package/package.json +1 -1
- package/src/bridge.ts +433 -417
- package/src/client/acp-ui.tsx +60 -32
- package/src/client/index.tsx +18 -0
- package/src/client/panel.tsx +395 -197
- package/src/client/permission-dock.tsx +133 -0
- package/src/client/session-controls.tsx +4 -4
- package/src/client/store.ts +112 -36
- package/src/impl.ts +166 -82
- package/src/index.ts +20 -65
- package/src/protocol.ts +143 -52
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
|
|
@@ -726,9 +754,10 @@ window.__ModuleLoader__.load({
|
|
|
726
754
|
async loadTranscript(sessionId) {
|
|
727
755
|
try {
|
|
728
756
|
const { session, entries } = await this.call("transcript/get", { sessionId });
|
|
757
|
+
const seen = this.lastSeq.get(sessionId) ?? 0;
|
|
758
|
+
if ((entries.at(-1)?.seq ?? 0) < seen) return;
|
|
729
759
|
const blocks = [];
|
|
730
760
|
for (const entry of entries) {
|
|
731
|
-
blocks.length;
|
|
732
761
|
foldUpdate(blocks, entry.update);
|
|
733
762
|
this.lastSeq.set(sessionId, Math.max(this.lastSeq.get(sessionId) ?? 0, entry.seq));
|
|
734
763
|
}
|
|
@@ -1112,6 +1141,14 @@ window.__ModuleLoader__.load({
|
|
|
1112
1141
|
this.flashError(error);
|
|
1113
1142
|
}
|
|
1114
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
|
+
*/
|
|
1115
1152
|
async respondPermission(requestId, optionId) {
|
|
1116
1153
|
try {
|
|
1117
1154
|
await this.call("permission/respond", {
|
|
@@ -1120,6 +1157,7 @@ window.__ModuleLoader__.load({
|
|
|
1120
1157
|
});
|
|
1121
1158
|
} catch (error) {
|
|
1122
1159
|
this.flashError(error);
|
|
1160
|
+
throw error;
|
|
1123
1161
|
}
|
|
1124
1162
|
}
|
|
1125
1163
|
async stopAgent(agentId) {
|
|
@@ -1160,7 +1198,7 @@ window.__ModuleLoader__.load({
|
|
|
1160
1198
|
}
|
|
1161
1199
|
}
|
|
1162
1200
|
/**
|
|
1163
|
-
* Write
|
|
1201
|
+
* Write the approval deadline (settings card), or drop the persisted override
|
|
1164
1202
|
* with `reset` so the `cordis.patch.yml` baseline applies again.
|
|
1165
1203
|
*
|
|
1166
1204
|
* The host answer — the same route returns the deadlines now in force — is the
|
|
@@ -1168,8 +1206,17 @@ window.__ModuleLoader__.load({
|
|
|
1168
1206
|
* merged with a value this browser did not know about) self-corrects instead
|
|
1169
1207
|
* of drifting from what runs.
|
|
1170
1208
|
*/
|
|
1171
|
-
async setApprovalDeadline(
|
|
1172
|
-
await this.writeApprovalDeadlines({
|
|
1209
|
+
async setApprovalDeadline(ms) {
|
|
1210
|
+
await this.writeApprovalDeadlines({ permissionTimeoutMs: ms });
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Write the mode: what an expired deadline DOES (`abort` refuses, `auto`
|
|
1214
|
+
* answers with the option the agent's own kinds imply, `wait` never
|
|
1215
|
+
* expires). Same write path as the window, so the host answer stays
|
|
1216
|
+
* the authority.
|
|
1217
|
+
*/
|
|
1218
|
+
async setApprovalTimeoutMode(mode) {
|
|
1219
|
+
await this.writeApprovalDeadlines({ mode });
|
|
1173
1220
|
}
|
|
1174
1221
|
/** Drop the persisted override: the config file (or the built-in default) decides again. */
|
|
1175
1222
|
async resetApprovalDeadlines() {
|
|
@@ -1179,8 +1226,8 @@ window.__ModuleLoader__.load({
|
|
|
1179
1226
|
const previous = this.state.approvalDeadlines;
|
|
1180
1227
|
if (previous !== void 0) this.mutate({ approvalDeadlines: {
|
|
1181
1228
|
...previous,
|
|
1182
|
-
...payload.
|
|
1183
|
-
...payload.
|
|
1229
|
+
...payload.permissionTimeoutMs === void 0 ? {} : { permissionTimeoutMs: payload.permissionTimeoutMs },
|
|
1230
|
+
...payload.mode === void 0 ? {} : { mode: payload.mode }
|
|
1184
1231
|
} });
|
|
1185
1232
|
try {
|
|
1186
1233
|
const result = await this.call("ui/approval-deadlines-set", payload);
|
|
@@ -1427,13 +1474,17 @@ window.__ModuleLoader__.load({
|
|
|
1427
1474
|
return this.loadAgentCatalog(event.agentId);
|
|
1428
1475
|
}
|
|
1429
1476
|
case "permission/request": {
|
|
1430
|
-
const
|
|
1431
|
-
this.mutate({ permissions: [...this.state.permissions.filter((p) => p.requestId !== event.requestId), {
|
|
1477
|
+
const permission = {
|
|
1432
1478
|
requestId: event.requestId,
|
|
1433
1479
|
sessionId: event.sessionId,
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
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] });
|
|
1437
1488
|
return Promise.resolve();
|
|
1438
1489
|
}
|
|
1439
1490
|
case "permission/resolved":
|
|
@@ -2370,9 +2421,9 @@ window.__ModuleLoader__.load({
|
|
|
2370
2421
|
.ntr-acp-seat:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2371
2422
|
.ntr-acp-seat:disabled { opacity: 0.5; cursor: default; }
|
|
2372
2423
|
.ntr-acp-seat.is-open { border-color: var(--dsw-alias-state-business-primary, #2563eb); color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
2373
|
-
.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); }
|
|
2374
2425
|
.ntr-acp-seat-main { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2375
|
-
.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; }
|
|
2376
2427
|
.ntr-acp-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
|
|
2377
2428
|
.ntr-acp-tick { margin-left: auto; color: var(--dsw-alias-state-business-primary, #2563eb); }
|
|
2378
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); }
|
|
@@ -2381,80 +2432,108 @@ window.__ModuleLoader__.load({
|
|
|
2381
2432
|
.ntr-acp-menu-head { padding: 8px 10px 6px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
|
|
2382
2433
|
.ntr-acp-menu-title { flex: 1; }
|
|
2383
2434
|
.ntr-acp-menu-spin { opacity: 0.5; }
|
|
2384
|
-
.ntr-acp-menu-note { padding: 0 10px 6px; font-size: 11px; color: var(--dsw-alias-
|
|
2385
|
-
.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; }
|
|
2386
2437
|
.ntr-acp-menu-body { overflow: auto; padding: 0 4px 6px; }
|
|
2387
2438
|
.ntr-acp-menu-section { display: flex; flex-direction: column; padding-top: 4px; }
|
|
2388
|
-
.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); }
|
|
2389
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%; }
|
|
2390
2441
|
.ntr-acp-menu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2391
2442
|
.ntr-acp-menu-item:disabled { opacity: 0.45; cursor: default; }
|
|
2392
2443
|
.ntr-acp-menu-item.is-active { font-weight: 600; }
|
|
2393
2444
|
.ntr-acp-menu-item-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2394
|
-
.ntr-acp-menu-item-detail { color: var(--dsw-alias-
|
|
2395
|
-
.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); }
|
|
2396
2447
|
/* ---- ACP view tab ---- */
|
|
2397
2448
|
.ntr-acp-view { display: flex; flex-direction: column; height: 100%; min-height: 0; font-size: 13px; line-height: 1.6; }
|
|
2398
|
-
.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); }
|
|
2399
2450
|
.ntr-acp-view-agent { font-weight: 600; color: inherit; }
|
|
2400
2451
|
.ntr-acp-view-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; direction: rtl; text-align: left; }
|
|
2401
2452
|
.ntr-acp-view-head .ntr-acp-more { margin-left: auto; }
|
|
2402
2453
|
.ntr-acp-stream { flex: 1; min-height: 0; overflow: auto; padding: 12px 14px 24px; display: flex; flex-direction: column; gap: 10px; }
|
|
2403
2454
|
.ntr-acp-badge { font-size: 11px; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; }
|
|
2404
|
-
.ntr-acp-badge.is-done { color: #
|
|
2405
|
-
.ntr-acp-badge.is-busy { color: #1d4ed8; background: #dbeafe; }
|
|
2406
|
-
.ntr-acp-badge.is-warn { color: #b45309; background: #fef3c7; }
|
|
2407
|
-
.ntr-acp-badge.is-fail { color: #b91c1c; background: #
|
|
2408
|
-
.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); }
|
|
2409
2460
|
.ntr-acp-bubble { max-width: 88%; padding: 8px 12px; border-radius: 12px; white-space: pre-wrap; word-break: break-word; }
|
|
2410
2461
|
.ntr-acp-bubble.is-user { align-self: flex-end; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 12%, transparent); }
|
|
2411
2462
|
.ntr-acp-bubble.is-agent { align-self: flex-start; background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
|
|
2412
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); }
|
|
2413
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; }
|
|
2414
|
-
.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; }
|
|
2415
2466
|
.ntr-acp-thought > summary { cursor: pointer; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
2416
2467
|
.ntr-acp-thought-body { white-space: pre-wrap; padding: 4px 0 2px; }
|
|
2417
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; }
|
|
2418
|
-
.ntr-acp-tool.is-fail { border-color: #fca5a5; }
|
|
2469
|
+
.ntr-acp-tool.is-fail { border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }
|
|
2419
2470
|
.ntr-acp-tool-head { display: flex; align-items: center; gap: 8px; padding: 7px 10px; font-size: 12px; }
|
|
2420
2471
|
.ntr-acp-tool-title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2421
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; }
|
|
2422
|
-
.ntr-acp-tool-input { background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-
|
|
2423
|
-
.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); }
|
|
2424
2475
|
.ntr-acp-diff { border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); }
|
|
2425
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); }
|
|
2426
2477
|
.ntr-acp-diff-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2427
|
-
.ntr-acp-diff-stats .is-add { color: #15803d; }
|
|
2428
|
-
.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); }
|
|
2429
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; }
|
|
2430
2481
|
.ntr-acp-diff-row { display: flex; gap: 8px; padding: 0 10px; white-space: pre; }
|
|
2431
|
-
.ntr-acp-diff-row.is-add { background: #dcfce7; }
|
|
2432
|
-
.ntr-acp-diff-row.is-del { background: #fee2e2; }
|
|
2433
|
-
.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); }
|
|
2434
2485
|
.ntr-acp-diff-sign { width: 1ch; flex: none; opacity: 0.7; }
|
|
2435
2486
|
.ntr-acp-diff-text { overflow-x: auto; }
|
|
2436
|
-
.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); }
|
|
2437
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; }
|
|
2438
2489
|
.ntr-acp-more:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }
|
|
2439
2490
|
.ntr-acp-more:disabled { opacity: 0.5; cursor: default; }
|
|
2440
|
-
.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); }
|
|
2441
2492
|
.ntr-acp-tool > .ntr-acp-more, .ntr-acp-empty-actions .ntr-acp-more { margin: 6px 10px; }
|
|
2442
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); }
|
|
2443
|
-
.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; }
|
|
2444
2495
|
.ntr-acp-plan-row { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; padding: 1px 0; }
|
|
2445
|
-
.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; }
|
|
2446
2497
|
.ntr-acp-plan-row.is-in_progress { font-weight: 600; }
|
|
2447
2498
|
.ntr-acp-plan-glyph { width: 1ch; flex: none; }
|
|
2448
|
-
.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); }
|
|
2449
2500
|
.ntr-acp-notice { padding: 6px 10px; border-radius: 8px; font-size: 12px; }
|
|
2450
|
-
.ntr-acp-notice.is-error { background: #
|
|
2451
|
-
.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); }
|
|
2452
2503
|
.ntr-acp-empty { margin: auto; max-width: 460px; text-align: center; display: flex; flex-direction: column; gap: 10px; padding: 24px; }
|
|
2453
2504
|
.ntr-acp-empty-title { font-size: 14px; font-weight: 600; }
|
|
2454
|
-
.ntr-acp-empty-body { margin: 0; color: var(--dsw-alias-
|
|
2455
|
-
.ntr-acp-empty-hint { color: var(--dsw-alias-
|
|
2456
|
-
.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; }
|
|
2457
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; }
|
|
2458
2537
|
/* ---- NTR settings dialog (shell.overlay) ---- */
|
|
2459
2538
|
/* Frame-wide surface opened from the agent base's empty-roster menu. The
|
|
2460
2539
|
overlay layer itself is click-through, so the layer opts back in and blocks
|
|
@@ -2491,11 +2570,11 @@ window.__ModuleLoader__.load({
|
|
|
2491
2570
|
* React surface for ACP: the settings configuration section.
|
|
2492
2571
|
*
|
|
2493
2572
|
* Scope is deliberately narrow — this is a CONFIGURATION surface. Runtime
|
|
2494
|
-
* interaction lives in the conversation: permission
|
|
2495
|
-
*
|
|
2496
|
-
*
|
|
2497
|
-
*
|
|
2498
|
-
*
|
|
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`).
|
|
2499
2578
|
*
|
|
2500
2579
|
* Each configured agent is one compact row (status + current model + badges)
|
|
2501
2580
|
* that expands into the full connection form and model catalog. The row stays
|
|
@@ -2527,8 +2606,10 @@ window.__ModuleLoader__.load({
|
|
|
2527
2606
|
--ntr-radius: 10px; --ntr-radius-sm: 7px;
|
|
2528
2607
|
--ntr-font: 13px; --ntr-font-sm: 12px; --ntr-font-xs: 11px;
|
|
2529
2608
|
--ntr-line: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
|
|
2530
|
-
--ntr-muted: var(--dsw-alias-
|
|
2531
|
-
--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));
|
|
2532
2613
|
--ntr-accent: var(--dsw-alias-button-primary-fill, #2563eb);
|
|
2533
2614
|
width: 100%; max-width: 960px; box-sizing: border-box; margin: 0 auto;
|
|
2534
2615
|
padding: 20px; overflow: auto; font-size: var(--ntr-font); line-height: 1.55;
|
|
@@ -2552,25 +2633,25 @@ window.__ModuleLoader__.load({
|
|
|
2552
2633
|
}
|
|
2553
2634
|
.dwa-btn:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); border-color: var(--dsw-alias-border-l1, #9ca3af); }
|
|
2554
2635
|
.dwa-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
2555
|
-
.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; }
|
|
2556
2637
|
.dwa-btn-primary:hover { background: var(--ntr-accent); border-color: transparent; opacity: 0.88; }
|
|
2557
2638
|
.dwa-btn-secondary { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }
|
|
2558
|
-
.dwa-btn-danger { color: var(--ntr-danger); border-color: #fca5a5; }
|
|
2559
|
-
.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); }
|
|
2560
2641
|
.dwa-section { margin-bottom: 24px; }
|
|
2561
2642
|
.dwa-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
|
|
2562
2643
|
.dwa-section-title { font-size: 14px; font-weight: 650; margin: 0; display: flex; align-items: center; gap: 8px; }
|
|
2563
2644
|
.dwa-section-copy { margin: 3px 0 0; font-size: var(--ntr-font-xs); color: var(--ntr-muted); }
|
|
2564
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; }
|
|
2565
|
-
.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); }
|
|
2566
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; }
|
|
2567
2648
|
.dwa-mode-title { display: flex; align-items: center; gap: 8px; font-weight: 650; }
|
|
2568
2649
|
.dwa-mode-copy { grid-column: 1 / -1; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
2569
2650
|
.dwa-mode-source { display: block; margin-top: 5px; overflow-wrap: anywhere; opacity: 0.85; }
|
|
2570
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; }
|
|
2571
|
-
.dwa-badge-success { background: #
|
|
2572
|
-
.dwa-badge-warn { background: #
|
|
2573
|
-
.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); }
|
|
2574
2655
|
.dwa-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
|
|
2575
2656
|
.dwa-agent-list { display: flex; flex-direction: column; gap: 8px; }
|
|
2576
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; }
|
|
@@ -2592,7 +2673,7 @@ window.__ModuleLoader__.load({
|
|
|
2592
2673
|
.dwa-subsection-copy { margin: 3px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
2593
2674
|
.dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: flex-start; }
|
|
2594
2675
|
.dwa-form-col { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
2595
|
-
.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); }
|
|
2596
2677
|
.dwa-hint { font-size: var(--ntr-font-xs); line-height: 1.55; color: var(--ntr-muted); }
|
|
2597
2678
|
.dwa-error { font-size: var(--ntr-font-xs); color: var(--ntr-danger); }
|
|
2598
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; }
|
|
@@ -2617,7 +2698,7 @@ window.__ModuleLoader__.load({
|
|
|
2617
2698
|
.dwa-add-content { border-top: var(--ntr-line); padding: 14px 16px 16px; }
|
|
2618
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; }
|
|
2619
2700
|
.dwa-chip:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
2620
|
-
.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; }
|
|
2621
2702
|
.dwa-chip-list { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }
|
|
2622
2703
|
.dwa-switch { display: inline-flex; align-items: center; gap: 7px; font-size: var(--ntr-font-sm); cursor: pointer; user-select: none; }
|
|
2623
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); }
|
|
@@ -2630,7 +2711,7 @@ window.__ModuleLoader__.load({
|
|
|
2630
2711
|
/* Tall enough for a comfortable selection (about ten rows) before scrolling;
|
|
2631
2712
|
the cap keeps the two stacked panes from pushing the rest of the card away. */
|
|
2632
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; }
|
|
2633
|
-
.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); }
|
|
2634
2715
|
.dwa-model-pane-head .dwa-btn { margin-left: auto; min-height: 26px; padding: 2px 8px; font-size: var(--ntr-font-xs); }
|
|
2635
2716
|
.dwa-model-pane-head .dwa-caret-btn { flex: none; width: 22px; height: 22px; background: transparent; }
|
|
2636
2717
|
.dwa-model-pane-head .dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); }
|
|
@@ -2648,7 +2729,7 @@ window.__ModuleLoader__.load({
|
|
|
2648
2729
|
.dwa-model-empty { padding: 6px; }
|
|
2649
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; }
|
|
2650
2731
|
.dwa-model-row:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
2651
|
-
.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); }
|
|
2652
2733
|
.dwa-model-row-mark { width: 12px; flex: none; text-align: center; opacity: 0.75; }
|
|
2653
2734
|
.dwa-model-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2654
2735
|
/* Name and credit keep their room; the id is the only cell that collapses. */
|
|
@@ -2670,15 +2751,31 @@ window.__ModuleLoader__.load({
|
|
|
2670
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; }
|
|
2671
2752
|
.dwa-seg > button + button { border-left: 1px solid var(--dsw-alias-border-l2, #d1d5db); }
|
|
2672
2753
|
.dwa-seg > button:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }
|
|
2673
|
-
.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; }
|
|
2674
2755
|
.dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; }
|
|
2675
2756
|
.dwa-preference-row + .dwa-preference-row { border-top: var(--ntr-line); }
|
|
2757
|
+
/* A control that needs the whole card: the label sits above it, not beside
|
|
2758
|
+
it. A right-aligned segmented control squeezed its labels into two lines
|
|
2759
|
+
each (中/止, 自动推/进), which read as broken rather than as choices. */
|
|
2760
|
+
.dwa-preference-block { padding: 12px 0; }
|
|
2761
|
+
.dwa-preference-block + .dwa-preference-block { border-top: var(--ntr-line); }
|
|
2676
2762
|
.dwa-preference-label { font-size: var(--ntr-font-sm); font-weight: 600; }
|
|
2677
2763
|
.dwa-preference-copy { margin-top: 2px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }
|
|
2678
|
-
.dwa-
|
|
2679
|
-
.dwa-
|
|
2680
|
-
|
|
2681
|
-
|
|
2764
|
+
.dwa-seg-wide { display: flex; width: 100%; margin-top: 10px; }
|
|
2765
|
+
.dwa-seg-wide > button { flex: 1; }
|
|
2766
|
+
/* The wait window, as a slider: the number is the headline, the rungs are the
|
|
2767
|
+
scale under the track. */
|
|
2768
|
+
.dwa-deadline-value { margin-top: 8px; font-size: 20px; font-weight: 600; }
|
|
2769
|
+
.dwa-deadline-range { width: 100%; margin-top: 6px; accent-color: var(--ntr-accent); }
|
|
2770
|
+
.dwa-deadline-range:disabled { opacity: 0.4; }
|
|
2771
|
+
.dwa-deadline-ticks { display: flex; justify-content: space-between; margin-top: 2px; color: var(--ntr-muted); font-size: 10px; }
|
|
2772
|
+
.dwa-deadline-ticks > span { white-space: nowrap; }
|
|
2773
|
+
/* The facts that make a number here meaningless, in warning ink rather than
|
|
2774
|
+
the muted gray every other hint uses: the value the config file is
|
|
2775
|
+
outranking, on the row it belongs to. */
|
|
2776
|
+
.dwa-deadline-flag { margin-top: 4px; font-size: var(--ntr-font-xs); font-weight: 550; color: var(--ntr-warn); }
|
|
2777
|
+
.dwa-deadline-warn { margin-top: 10px; padding: 9px 12px; border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-secondary, #fffbeb); border-radius: var(--ntr-radius-sm); font-size: var(--ntr-font-xs); line-height: 1.55; }
|
|
2778
|
+
.dwa-deadline-reset { display: flex; justify-content: flex-end; margin-top: 8px; }
|
|
2682
2779
|
@media (max-width: 640px) {
|
|
2683
2780
|
.dwa-root { padding: 14px; }
|
|
2684
2781
|
.dwa-page-header { flex-direction: column; }
|
|
@@ -2688,6 +2785,11 @@ window.__ModuleLoader__.load({
|
|
|
2688
2785
|
.dwa-model-search { flex: 1 1 auto; width: auto; }
|
|
2689
2786
|
.dwa-subsection-head, .dwa-preference-row { align-items: flex-start; flex-direction: column; }
|
|
2690
2787
|
.dwa-default-hint { align-items: flex-start; flex-direction: column; }
|
|
2788
|
+
/* The slider's scale: up to eight rungs with full unit words ("15 分钟")
|
|
2789
|
+
need a smaller face before they can collide on a narrow panel. The rung
|
|
2790
|
+
count follows the mode (自动推进 adds 不等待), and the row is a flex
|
|
2791
|
+
space-between, so a shorter scale just spreads wider. */
|
|
2792
|
+
.dwa-deadline-ticks { font-size: 9px; }
|
|
2691
2793
|
}
|
|
2692
2794
|
@media (prefers-reduced-motion: reduce) {
|
|
2693
2795
|
.dwa-root *, .dwa-root *::before, .dwa-root *::after { transition: none !important; }
|
|
@@ -2708,19 +2810,19 @@ window.__ModuleLoader__.load({
|
|
|
2708
2810
|
function stateVisual(state, acp) {
|
|
2709
2811
|
switch (state) {
|
|
2710
2812
|
case "ready": return {
|
|
2711
|
-
color: "#22c55e",
|
|
2813
|
+
color: "var(--dsw-alias-state-success-primary, #22c55e)",
|
|
2712
2814
|
text: acp.label("已连接", "Connected")
|
|
2713
2815
|
};
|
|
2714
2816
|
case "starting": return {
|
|
2715
|
-
color: "#eab308",
|
|
2817
|
+
color: "var(--dsw-alias-state-warn-primary, #eab308)",
|
|
2716
2818
|
text: acp.label("启动中", "Starting")
|
|
2717
2819
|
};
|
|
2718
2820
|
case "error": return {
|
|
2719
|
-
color: "#ef4444",
|
|
2821
|
+
color: "var(--dsw-alias-state-error-primary, #ef4444)",
|
|
2720
2822
|
text: acp.label("错误", "Error")
|
|
2721
2823
|
};
|
|
2722
2824
|
default: return {
|
|
2723
|
-
color: "#9ca3af",
|
|
2825
|
+
color: "var(--dsw-alias-state-idle-primary, #9ca3af)",
|
|
2724
2826
|
text: acp.label("已配置", "Configured"),
|
|
2725
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.")
|
|
2726
2828
|
};
|
|
@@ -2893,139 +2995,270 @@ window.__ModuleLoader__.load({
|
|
|
2893
2995
|
/**
|
|
2894
2996
|
* The settings panel's permission-ledger rule.
|
|
2895
2997
|
*
|
|
2896
|
-
*
|
|
2897
|
-
*
|
|
2898
|
-
*
|
|
2899
|
-
*
|
|
2900
|
-
*
|
|
2901
|
-
*
|
|
2902
|
-
* (`approvalRouted` → a read-only count, no buttons) hid the only answer
|
|
2903
|
-
* entry exactly when the native route had degraded to unavailable, and the
|
|
2904
|
-
* ask then hung until its timeout.
|
|
2905
|
-
*
|
|
2906
|
-
* `fellBackFromConversation` is the case the card has to explain: the host
|
|
2907
|
-
* 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.
|
|
2908
3004
|
*/
|
|
2909
3005
|
function permissionLedgerCard(ui) {
|
|
2910
|
-
|
|
2911
|
-
return {
|
|
2912
|
-
visible,
|
|
2913
|
-
fellBackFromConversation: visible && ui.approvalRouted
|
|
2914
|
-
};
|
|
3006
|
+
return { visible: ui.permissions.length > 0 };
|
|
2915
3007
|
}
|
|
2916
3008
|
/**
|
|
2917
|
-
*
|
|
2918
|
-
*
|
|
2919
|
-
*
|
|
3009
|
+
* The ONE wait window, as the rungs the slider snaps to.
|
|
3010
|
+
*
|
|
3011
|
+
* Shortest wait first, and `0` (不等待) FIRST: the slider reads left to right
|
|
3012
|
+
* as "wait longer", and "do not wait at all" is the shortest wait there is.
|
|
3013
|
+
* Never expiring is NOT on this scale — that is the `wait` MODE's business,
|
|
3014
|
+
* and having 「一直等待」 in both places made the card say the same thing twice
|
|
3015
|
+
* in two ways.
|
|
3016
|
+
*
|
|
3017
|
+
* A window the config file names that is not on this ladder (45 seconds, say)
|
|
3018
|
+
* still runs as configured: the card prints the exact number and parks the
|
|
3019
|
+
* thumb on the nearest rung, so the slider never pretends a number the host
|
|
3020
|
+
* is not using.
|
|
2920
3021
|
*/
|
|
2921
|
-
const
|
|
3022
|
+
const DEADLINE_LADDER = [
|
|
2922
3023
|
0,
|
|
2923
3024
|
3e4,
|
|
2924
3025
|
6e4,
|
|
2925
3026
|
3e5,
|
|
2926
|
-
9e5
|
|
3027
|
+
9e5,
|
|
3028
|
+
18e5,
|
|
3029
|
+
36e5,
|
|
3030
|
+
216e5
|
|
3031
|
+
];
|
|
3032
|
+
/**
|
|
3033
|
+
* The shortest REAL wait — the first rung above 不等待.
|
|
3034
|
+
*
|
|
3035
|
+
* Hand-copied from the host half (`APPROVAL_DEADLINE_MIN_WAIT_MS` in
|
|
3036
|
+
* `src/impl.ts`): the browser half may not import the host module, so the
|
|
3037
|
+
* equivalence is pinned by a test instead (see `tests/acp-view.spec.ts`).
|
|
3038
|
+
*/
|
|
3039
|
+
const DEADLINE_MIN_WAIT_MS = 3e4;
|
|
3040
|
+
/**
|
|
3041
|
+
* The window the card shows under a mode.
|
|
3042
|
+
*
|
|
3043
|
+
* 不等待 (`0`) is the **auto** mode's rung: it answers with the option the
|
|
3044
|
+
* agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —
|
|
3045
|
+
* refusing every ask the instant it arrives stops the agent at every gate it
|
|
3046
|
+
* meets, which is not a timeout but a wall. So under `abort` the window floors
|
|
3047
|
+
* to the shortest real wait, and the card never shows a zero it cannot run.
|
|
3048
|
+
*
|
|
3049
|
+
* `undefined` (the `wait` mode) stays undefined: there is no window at all.
|
|
3050
|
+
*/
|
|
3051
|
+
function deadlineValueFor(mode, ms) {
|
|
3052
|
+
if (ms === void 0) return void 0;
|
|
3053
|
+
return mode === "abort" ? Math.max(ms, DEADLINE_MIN_WAIT_MS) : ms;
|
|
3054
|
+
}
|
|
3055
|
+
/**
|
|
3056
|
+
* The rungs a mode offers.
|
|
3057
|
+
*
|
|
3058
|
+
* 不等待 (`0`) is the **auto** mode's rung: it answers with the option the
|
|
3059
|
+
* agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —
|
|
3060
|
+
* refusing every ask the instant it arrives stops the agent at every gate it
|
|
3061
|
+
* meets, which is not a timeout but a wall. So `abort` starts at the shortest
|
|
3062
|
+
* real wait.
|
|
3063
|
+
*/
|
|
3064
|
+
function deadlineLadder(mode) {
|
|
3065
|
+
return mode === "auto" ? DEADLINE_LADDER : DEADLINE_LADDER.filter((ms) => ms > 0);
|
|
3066
|
+
}
|
|
3067
|
+
/**
|
|
3068
|
+
* The rung the slider shows for a window. Exact hits keep their rung; anything
|
|
3069
|
+
* else takes the nearest one. A tie goes to the SHORTER wait: the thumb must
|
|
3070
|
+
* never promise more patience than the window has.
|
|
3071
|
+
*/
|
|
3072
|
+
function deadlineSliderIndex(ms, mode = "auto") {
|
|
3073
|
+
const ladder = deadlineLadder(mode);
|
|
3074
|
+
const exact = ladder.indexOf(ms);
|
|
3075
|
+
if (exact >= 0) return exact;
|
|
3076
|
+
let best = 0;
|
|
3077
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
3078
|
+
for (let i = 0; i < ladder.length; i++) {
|
|
3079
|
+
const distance = Math.abs(ms - ladder[i]);
|
|
3080
|
+
if (distance < bestDistance) {
|
|
3081
|
+
bestDistance = distance;
|
|
3082
|
+
best = i;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
return best;
|
|
3086
|
+
}
|
|
3087
|
+
/** The three modes, in the order a user reads them: refuse, answer, wait. */
|
|
3088
|
+
const DEADLINE_MODES = [
|
|
3089
|
+
"abort",
|
|
3090
|
+
"auto",
|
|
3091
|
+
"wait"
|
|
2927
3092
|
];
|
|
2928
|
-
/**
|
|
2929
|
-
const DEADLINE_FIELDS = ["nativeApprovalTimeoutMs", "permissionFallbackTimeoutMs"];
|
|
2930
|
-
/** `5 分钟` / `30 秒` / `关` — the readout and the preset label, one formatter. */
|
|
3093
|
+
/** `5 分钟` / `30 秒` / `不等待` — the readout and the tick label, one formatter. */
|
|
2931
3094
|
function deadlineText(ms, acp) {
|
|
2932
|
-
if (ms <= 0) return acp.label("
|
|
3095
|
+
if (ms <= 0) return acp.label("不等待", "No wait");
|
|
2933
3096
|
const seconds = Math.round(ms / 1e3);
|
|
2934
3097
|
if (seconds < 60) return acp.label(`${String(seconds)} 秒`, `${String(seconds)}s`);
|
|
2935
3098
|
const minutes = Math.round(seconds / 60);
|
|
2936
|
-
return acp.label(`${String(minutes)}
|
|
3099
|
+
if (minutes < 60) return acp.label(`${String(minutes)} 分钟`, `${String(minutes)}m`);
|
|
3100
|
+
const hours = Math.round(minutes / 60);
|
|
3101
|
+
return acp.label(`${String(hours)} 小时`, `${String(hours)}h`);
|
|
3102
|
+
}
|
|
3103
|
+
/** The mode's label — one formatter, so the picker and the override note agree. */
|
|
3104
|
+
function modeText(mode, acp) {
|
|
3105
|
+
if (mode === "abort") return acp.label("超时中止", "Abort");
|
|
3106
|
+
if (mode === "auto") return acp.label("自动推进", "Auto");
|
|
3107
|
+
return acp.label("一直等待", "Wait");
|
|
2937
3108
|
}
|
|
2938
3109
|
/**
|
|
2939
|
-
*
|
|
2940
|
-
*
|
|
3110
|
+
* The mode block: what happens when nobody answers.
|
|
3111
|
+
*
|
|
3112
|
+
* Three values and no more — refuse, answer, wait — so the picker is a
|
|
3113
|
+
* segmented control, not a dropdown. The buttons stretch across the whole
|
|
3114
|
+
* card: a right-aligned segmented control squeezed the labels into two lines
|
|
3115
|
+
* each (中/止, 自动推/进), which read as broken rather than as choices.
|
|
2941
3116
|
*
|
|
2942
|
-
* The
|
|
2943
|
-
*
|
|
2944
|
-
*
|
|
2945
|
-
* shows, so a value the host trims never lingers in the UI.
|
|
3117
|
+
* The copy under the buttons says what the SELECTED mode does, in one
|
|
3118
|
+
* sentence — the labels alone ("超时中止") cannot. `auto` carries a warning of
|
|
3119
|
+
* its own: it approves things nobody looked at.
|
|
2946
3120
|
*/
|
|
2947
|
-
function
|
|
2948
|
-
const { acp,
|
|
2949
|
-
const
|
|
2950
|
-
const text = draft ?? String(Math.round(value / 1e3));
|
|
2951
|
-
const parsed = /^\d+$/.test(text.trim()) ? Number(text.trim()) * 1e3 : void 0;
|
|
2952
|
-
const commit = () => {
|
|
2953
|
-
const next = parsed;
|
|
2954
|
-
setDraft(void 0);
|
|
2955
|
-
if (next !== void 0 && next !== value) acp.setApprovalDeadline(field, next);
|
|
2956
|
-
};
|
|
3121
|
+
function DeadlineModeBlock(props) {
|
|
3122
|
+
const { acp, mode, override } = props;
|
|
3123
|
+
const copy = mode === "wait" ? acp.label("一直等待应答,不设超时时间。", "Wait until you answer — no deadline.") : mode === "auto" ? acp.label("超过设定时间未应答,自动代替你选择一个选项让 agent 继续执行。", "Past the deadline, an option is chosen for you and the agent continues.") : acp.label("超过设定时间未应答,自动拒绝该操作,agent 继续执行。", "Past the deadline, the operation is refused and the agent continues.");
|
|
2957
3124
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2958
|
-
className: "dwa-preference-
|
|
2959
|
-
children: [
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
children:
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
onBlur: commit,
|
|
2987
|
-
onKeyDown: (event) => {
|
|
2988
|
-
if (event.key === "Enter") commit();
|
|
2989
|
-
}
|
|
2990
|
-
}),
|
|
2991
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2992
|
-
className: "dwa-hint dwa-deadline-unit",
|
|
2993
|
-
children: acp.label("秒", "sec")
|
|
2994
|
-
})
|
|
2995
|
-
]
|
|
2996
|
-
})]
|
|
3125
|
+
className: "dwa-preference-block",
|
|
3126
|
+
children: [
|
|
3127
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3128
|
+
className: "dwa-preference-label",
|
|
3129
|
+
children: acp.label("超时处理方式", "When nobody answers")
|
|
3130
|
+
}),
|
|
3131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3132
|
+
className: "dwa-seg dwa-seg-wide",
|
|
3133
|
+
children: DEADLINE_MODES.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3134
|
+
type: "button",
|
|
3135
|
+
className: mode === option ? "is-on" : "",
|
|
3136
|
+
onClick: () => void acp.setApprovalTimeoutMode(option),
|
|
3137
|
+
children: modeText(option, acp)
|
|
3138
|
+
}, option))
|
|
3139
|
+
}),
|
|
3140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3141
|
+
className: "dwa-preference-copy",
|
|
3142
|
+
children: copy
|
|
3143
|
+
}),
|
|
3144
|
+
mode === "auto" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3145
|
+
className: "dwa-deadline-warn",
|
|
3146
|
+
children: acp.label("请确保你的 agent 值得信任,请谨慎选择。", "Only pick this if you trust your agent — choose it carefully.")
|
|
3147
|
+
}),
|
|
3148
|
+
override !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3149
|
+
className: "dwa-deadline-flag",
|
|
3150
|
+
children: ["⚠ ", override]
|
|
3151
|
+
})
|
|
3152
|
+
]
|
|
2997
3153
|
});
|
|
2998
3154
|
}
|
|
2999
3155
|
/**
|
|
3156
|
+
* The duration block: how long to wait before the deadline fires.
|
|
3157
|
+
*
|
|
3158
|
+
* ONE slider, because there is ONE window: an ask in the conversation and an
|
|
3159
|
+
* ask in the settings ledger are the same question, so they get the same
|
|
3160
|
+
* answer. The slider's rungs are the windows people actually pick; a window
|
|
3161
|
+
* the config file names outside them still runs as configured (the readout
|
|
3162
|
+
* prints the exact number and the thumb parks on the nearest rung).
|
|
3163
|
+
*
|
|
3164
|
+
* `value === undefined` is the `wait` mode: there is no deadline at all, so
|
|
3165
|
+
* the scale has nothing to say — the slider is not drawn, and the readout
|
|
3166
|
+
* names the state that is actually in force. The stored window is NOT touched
|
|
3167
|
+
* by that (the mode write carries only the mode), so switching back restores
|
|
3168
|
+
* it.
|
|
3169
|
+
*/
|
|
3170
|
+
function DeadlineSliderBlock(props) {
|
|
3171
|
+
const { acp, value, mode, override } = props;
|
|
3172
|
+
const effective = deadlineValueFor(mode, value);
|
|
3173
|
+
const ladder = deadlineLadder(mode);
|
|
3174
|
+
const index = effective === void 0 ? 0 : deadlineSliderIndex(effective, mode);
|
|
3175
|
+
const copy = effective === void 0 ? acp.label("选择「一直等待」时不设超时时长。", "“Wait” sets no deadline.") : effective <= 0 ? acp.label("不设等待时间,请求到达即按上方所选的方式处理。", "No wait: the ask is settled on arrival, by whatever you picked above.") : acp.label("超过该时长仍未应答,将按上方所选的方式处理。", "Past this, whatever you picked above is what happens.");
|
|
3176
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3177
|
+
className: "dwa-preference-block",
|
|
3178
|
+
children: [
|
|
3179
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3180
|
+
className: "dwa-preference-label",
|
|
3181
|
+
children: acp.label("等待时长", "How long to wait")
|
|
3182
|
+
}),
|
|
3183
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3184
|
+
className: "dwa-deadline-value",
|
|
3185
|
+
children: effective === void 0 ? acp.label("一直等待", "Always") : deadlineText(effective, acp)
|
|
3186
|
+
}),
|
|
3187
|
+
effective !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3188
|
+
className: "dwa-deadline-range",
|
|
3189
|
+
type: "range",
|
|
3190
|
+
min: 0,
|
|
3191
|
+
max: ladder.length - 1,
|
|
3192
|
+
step: 1,
|
|
3193
|
+
value: index,
|
|
3194
|
+
"aria-label": acp.label("等待时长", "How long to wait"),
|
|
3195
|
+
onChange: (event) => {
|
|
3196
|
+
const next = ladder[Number(event.target.value)];
|
|
3197
|
+
if (next !== void 0 && next !== effective) acp.setApprovalDeadline(next);
|
|
3198
|
+
}
|
|
3199
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3200
|
+
className: "dwa-deadline-ticks",
|
|
3201
|
+
children: ladder.map((rung) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: deadlineText(rung, acp) }, rung))
|
|
3202
|
+
})] }),
|
|
3203
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3204
|
+
className: "dwa-preference-copy",
|
|
3205
|
+
children: copy
|
|
3206
|
+
}),
|
|
3207
|
+
override !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3208
|
+
className: "dwa-deadline-flag",
|
|
3209
|
+
children: ["⚠ ", override]
|
|
3210
|
+
})
|
|
3211
|
+
]
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3214
|
+
/**
|
|
3215
|
+
* The facts the card has to show, decided from what the host reported.
|
|
3216
|
+
*
|
|
3217
|
+
* The card carries TWO controls, and they are orthogonal: one window (how
|
|
3218
|
+
* long) and one mode (what happens). An override can sit on either of them,
|
|
3219
|
+
* so each is decided on its own.
|
|
3220
|
+
*
|
|
3221
|
+
* `overridden` is what gates the reset button. A baseline that AGREES with the
|
|
3222
|
+
* running value is not an override, and offering a reset for it would be a
|
|
3223
|
+
* button that does nothing (observed live).
|
|
3224
|
+
*/
|
|
3225
|
+
function approvalDeadlineFacts(deadlines) {
|
|
3226
|
+
const configured = deadlines.configured?.permissionTimeoutMs;
|
|
3227
|
+
const baseline = configured === void 0 || configured === deadlines.permissionTimeoutMs ? void 0 : configured;
|
|
3228
|
+
const modeBaseline = deadlines.configuredMode !== void 0 && deadlines.configuredMode !== deadlines.mode ? deadlines.configuredMode : void 0;
|
|
3229
|
+
return {
|
|
3230
|
+
baseline,
|
|
3231
|
+
modeBaseline,
|
|
3232
|
+
overridden: baseline !== void 0 || modeBaseline !== void 0
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3000
3236
|
* The approval-deadline card.
|
|
3001
3237
|
*
|
|
3002
|
-
*
|
|
3003
|
-
*
|
|
3004
|
-
*
|
|
3005
|
-
*
|
|
3006
|
-
*
|
|
3007
|
-
*
|
|
3008
|
-
*
|
|
3238
|
+
* TWO controls, and they are orthogonal: one window (how long) and one mode
|
|
3239
|
+
* (what happens). One window covers both places an ask can wait — the
|
|
3240
|
+
* conversation's approval card and the settings panel's ledger — because they
|
|
3241
|
+
* ask the same question and the answer should not differ by where the card
|
|
3242
|
+
* happens to be drawn.
|
|
3243
|
+
*
|
|
3244
|
+
* The two controls are ONE system, and the card says so in a sentence: the
|
|
3245
|
+
* window says how long to wait, the mode says what happens when nobody
|
|
3246
|
+
* answered. That sentence is the thing the previous three-row layout kept
|
|
3247
|
+
* missing.
|
|
3009
3248
|
*
|
|
3010
|
-
*
|
|
3011
|
-
*
|
|
3012
|
-
*
|
|
3249
|
+
* The facts that make a number here meaningless are shown, not swallowed: the
|
|
3250
|
+
* deployment baseline this card outranks. Each rides on the control it belongs
|
|
3251
|
+
* to, and the reset button appears only when something is actually being
|
|
3252
|
+
* outranked.
|
|
3013
3253
|
*/
|
|
3014
3254
|
function ApprovalDeadlinesCard(props) {
|
|
3015
3255
|
const { acp, ui } = props;
|
|
3016
3256
|
const deadlines = ui.approvalDeadlines;
|
|
3017
3257
|
if (deadlines === void 0) return null;
|
|
3018
|
-
const
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
};
|
|
3023
|
-
const overrideNote = (field) => {
|
|
3024
|
-
const baseline = configured[field];
|
|
3025
|
-
if (baseline === void 0 || baseline === inForce[field]) return void 0;
|
|
3026
|
-
return acp.label(`cordis.patch.yml 里是 ${deadlineText(baseline, acp)},设置页的值优先。`, `cordis.patch.yml asks for ${deadlineText(baseline, acp)}; the value set here wins.`);
|
|
3027
|
-
};
|
|
3028
|
-
const overridden = DEADLINE_FIELDS.some((field) => overrideNote(field) !== void 0);
|
|
3258
|
+
const facts = approvalDeadlineFacts(deadlines);
|
|
3259
|
+
const baselineNote = facts.baseline === void 0 ? void 0 : acp.label(`配置文件(cordis.patch.yml)设为 ${deadlineText(facts.baseline, acp)},此处设置优先。`, `The config file (cordis.patch.yml) asks for ${deadlineText(facts.baseline, acp)}; the value set here wins.`);
|
|
3260
|
+
const modeNote = facts.modeBaseline === void 0 ? void 0 : acp.label(`配置文件(cordis.patch.yml)设为「${modeText(facts.modeBaseline, acp)}」,此处设置优先。`, `The config file (cordis.patch.yml) asks for "${modeText(facts.modeBaseline, acp)}"; the value set here wins.`);
|
|
3261
|
+
const waitMode = deadlines.mode === "wait";
|
|
3029
3262
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3030
3263
|
className: "dwa-section",
|
|
3031
3264
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -3035,7 +3268,7 @@ window.__ModuleLoader__.load({
|
|
|
3035
3268
|
children: acp.label("审批时限", "Approval deadlines")
|
|
3036
3269
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3037
3270
|
className: "dwa-section-copy",
|
|
3038
|
-
children: acp.label("
|
|
3271
|
+
children: acp.label("权限请求的等待时长与超时处理方式。", "How long to wait for a permission request, and what happens when nobody answers.")
|
|
3039
3272
|
})] })
|
|
3040
3273
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3041
3274
|
className: "dwa-card",
|
|
@@ -3044,46 +3277,25 @@ window.__ModuleLoader__.load({
|
|
|
3044
3277
|
paddingBottom: 4
|
|
3045
3278
|
},
|
|
3046
3279
|
children: [
|
|
3047
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3280
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeadlineModeBlock, {
|
|
3048
3281
|
acp,
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
copy: acp.label("宿主把询问接进对话后,多久没出现就撤回并改到设置页问。会渲染的接管是立即出现的,所以这个期限只为「收了却不渲染」而响。", "How long an ask the host took into the composer may stay invisible before it is withdrawn and re-asked in Settings. A takeover that renders does so immediately, so this only fires for one that never showed up."),
|
|
3052
|
-
value: deadlines.nativeApprovalTimeoutMs
|
|
3282
|
+
mode: deadlines.mode,
|
|
3283
|
+
override: modeNote
|
|
3053
3284
|
}),
|
|
3054
|
-
|
|
3055
|
-
className: "dwa-hint",
|
|
3056
|
-
style: { paddingBottom: 8 },
|
|
3057
|
-
children: overrideNote("nativeApprovalTimeoutMs")
|
|
3058
|
-
}),
|
|
3059
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeadlineRow, {
|
|
3285
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeadlineSliderBlock, {
|
|
3060
3286
|
acp,
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
value: deadlines.permissionFallbackTimeoutMs
|
|
3065
|
-
}),
|
|
3066
|
-
overrideNote("permissionFallbackTimeoutMs") !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3067
|
-
className: "dwa-hint",
|
|
3068
|
-
style: { paddingBottom: 8 },
|
|
3069
|
-
children: overrideNote("permissionFallbackTimeoutMs")
|
|
3287
|
+
value: waitMode ? void 0 : deadlines.permissionTimeoutMs,
|
|
3288
|
+
mode: deadlines.mode,
|
|
3289
|
+
override: baselineNote
|
|
3070
3290
|
}),
|
|
3071
|
-
|
|
3072
|
-
className: "dwa-
|
|
3073
|
-
|
|
3074
|
-
children: acp.label(`注意:cordis.patch.yml 里 permissionTimeoutMs = ${deadlineText(deadlines.permissionTimeoutMs, acp)},它一视同仁,覆盖上面两个窗口。`, `Note: cordis.patch.yml sets permissionTimeoutMs = ${deadlineText(deadlines.permissionTimeoutMs, acp)}, which governs every path alike and overrides both windows above.`)
|
|
3075
|
-
}),
|
|
3076
|
-
overridden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3077
|
-
className: "dwa-preference-row",
|
|
3078
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3079
|
-
className: "dwa-preference-copy",
|
|
3080
|
-
children: acp.label("设置页的值写在插件自己的状态文件里,重启后仍然有效;重置后回到 cordis.patch.yml 的配置。", "A value set here lives in the plugin's own state file and survives a restart; resetting returns to whatever cordis.patch.yml asks for.")
|
|
3081
|
-
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3291
|
+
facts.overridden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3292
|
+
className: "dwa-deadline-reset",
|
|
3293
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3082
3294
|
type: "button",
|
|
3083
|
-
className: "dwa-btn",
|
|
3295
|
+
className: "dwa-btn dwa-btn-secondary",
|
|
3084
3296
|
onClick: () => void acp.resetApprovalDeadlines(),
|
|
3085
3297
|
children: acp.label("恢复配置文件的值", "Use config file value")
|
|
3086
|
-
})
|
|
3298
|
+
})
|
|
3087
3299
|
})
|
|
3088
3300
|
]
|
|
3089
3301
|
})]
|
|
@@ -3121,7 +3333,7 @@ window.__ModuleLoader__.load({
|
|
|
3121
3333
|
children: [
|
|
3122
3334
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3123
3335
|
className: "dwa-dot",
|
|
3124
|
-
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)" },
|
|
3125
3337
|
title: ui.stream
|
|
3126
3338
|
}),
|
|
3127
3339
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: streamText(ui.stream, acp) }),
|
|
@@ -3149,14 +3361,14 @@ window.__ModuleLoader__.load({
|
|
|
3149
3361
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3150
3362
|
style: {
|
|
3151
3363
|
fontWeight: 600,
|
|
3152
|
-
marginBottom:
|
|
3364
|
+
marginBottom: 4
|
|
3153
3365
|
},
|
|
3154
3366
|
children: acp.label("待处理权限请求", "Pending Permission Requests")
|
|
3155
3367
|
}),
|
|
3156
|
-
|
|
3368
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3157
3369
|
className: "dwa-hint",
|
|
3158
3370
|
style: { marginBottom: 8 },
|
|
3159
|
-
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.")
|
|
3160
3372
|
}),
|
|
3161
3373
|
ui.permissions.map((permission) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3162
3374
|
style: {
|
|
@@ -3167,21 +3379,31 @@ window.__ModuleLoader__.load({
|
|
|
3167
3379
|
flexWrap: "wrap"
|
|
3168
3380
|
},
|
|
3169
3381
|
children: [
|
|
3170
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3382
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3171
3383
|
style: {
|
|
3172
3384
|
flex: 1,
|
|
3173
3385
|
minWidth: 160
|
|
3174
3386
|
},
|
|
3175
|
-
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
|
+
})]
|
|
3176
3394
|
}),
|
|
3177
3395
|
permission.options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3178
3396
|
className: "dwa-btn dwa-btn-primary",
|
|
3179
|
-
onClick: () =>
|
|
3397
|
+
onClick: () => {
|
|
3398
|
+
acp.respondPermission(permission.requestId, option.optionId).catch(() => void 0);
|
|
3399
|
+
},
|
|
3180
3400
|
children: option.name
|
|
3181
3401
|
}, option.optionId)),
|
|
3182
3402
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3183
3403
|
className: "dwa-btn",
|
|
3184
|
-
onClick: () =>
|
|
3404
|
+
onClick: () => {
|
|
3405
|
+
acp.respondPermission(permission.requestId).catch(() => void 0);
|
|
3406
|
+
},
|
|
3185
3407
|
children: acp.label("拒绝", "Dismiss")
|
|
3186
3408
|
})
|
|
3187
3409
|
]
|
|
@@ -3871,7 +4093,7 @@ window.__ModuleLoader__.load({
|
|
|
3871
4093
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("code", {
|
|
3872
4094
|
className: "dwa-input-mono",
|
|
3873
4095
|
style: {
|
|
3874
|
-
background: "#0000000a",
|
|
4096
|
+
background: "var(--dsw-alias-markdown-inline-code, #0000000a)",
|
|
3875
4097
|
padding: "2px 6px",
|
|
3876
4098
|
borderRadius: 4
|
|
3877
4099
|
},
|
|
@@ -3927,7 +4149,13 @@ window.__ModuleLoader__.load({
|
|
|
3927
4149
|
}),
|
|
3928
4150
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3929
4151
|
className: idleInvalid ? "dwa-error" : "dwa-hint",
|
|
3930
|
-
children: idleInvalid ? acp.label("必须是 0(永不释放)或 [1000, 86400000] 之间的整数毫秒。", "Must be 0 (never release) or an integer in [1000, 86400000] ms.") :
|
|
4152
|
+
children: idleInvalid ? acp.label("必须是 0(永不释放)或 [1000, 86400000] 之间的整数毫秒。", "Must be 0 (never release) or an integer in [1000, 86400000] ms.") : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4153
|
+
acp.label("空闲超过此时长就挂起进程(会话保留,下一条消息自动恢复)。", "The process is parked once it has been idle this long (the session is kept and resumes on the next message)."),
|
|
4154
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
|
|
4155
|
+
acp.label("0 = 永不释放;回合在跑、取消未结算或审批待答时不释放。", "0 = never; a process with a running turn, an unsettled cancel, or a pending approval is never released."),
|
|
4156
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
|
|
4157
|
+
acp.label("只对承载过会话的进程生效;探测进程固定 5 分钟释放,填 0 也拦不住。", "This window applies only to a process that has hosted a session; the model-probe process is always released after 5 minutes, and 0 does not stop it.")
|
|
4158
|
+
] })
|
|
3931
4159
|
})
|
|
3932
4160
|
]
|
|
3933
4161
|
})
|
|
@@ -4064,7 +4292,7 @@ window.__ModuleLoader__.load({
|
|
|
4064
4292
|
fontSize: 12,
|
|
4065
4293
|
whiteSpace: "pre-wrap",
|
|
4066
4294
|
wordBreak: "break-all",
|
|
4067
|
-
color: testResult.ok ? "
|
|
4295
|
+
color: testResult.ok ? "var(--ntr-success)" : "var(--ntr-danger)"
|
|
4068
4296
|
},
|
|
4069
4297
|
children: [testResult.ok ? "✓ " : "✕ ", testResult.text]
|
|
4070
4298
|
})
|
|
@@ -4453,7 +4681,7 @@ window.__ModuleLoader__.load({
|
|
|
4453
4681
|
onChange: (e) => void toggleAutoDiscover(e.target.checked),
|
|
4454
4682
|
style: {
|
|
4455
4683
|
margin: 0,
|
|
4456
|
-
accentColor: "#2563eb"
|
|
4684
|
+
accentColor: "var(--dsw-alias-state-business-primary, #2563eb)"
|
|
4457
4685
|
}
|
|
4458
4686
|
}), acp.label("自动发现新模型", "Auto-discover new models")]
|
|
4459
4687
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -4931,10 +5159,10 @@ window.__ModuleLoader__.load({
|
|
|
4931
5159
|
* the entry point for conversations no agent has served.
|
|
4932
5160
|
*/
|
|
4933
5161
|
const STATE_COLORS = {
|
|
4934
|
-
ready: "#22c55e",
|
|
4935
|
-
starting: "#eab308",
|
|
4936
|
-
error: "#ef4444",
|
|
4937
|
-
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)"
|
|
4938
5166
|
};
|
|
4939
5167
|
function agentStateText(acp, state) {
|
|
4940
5168
|
switch (state) {
|
|
@@ -6308,6 +6536,130 @@ window.__ModuleLoader__.load({
|
|
|
6308
6536
|
});
|
|
6309
6537
|
}
|
|
6310
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
|
|
6311
6663
|
//#region src/client/model-seat.tsx
|
|
6312
6664
|
/** Exact key of the composer's model seat (a `single`, session-scoped slot). */
|
|
6313
6665
|
const MODEL_SEAT_KEY = "conversation.input.model";
|
|
@@ -6968,6 +7320,12 @@ window.__ModuleLoader__.load({
|
|
|
6968
7320
|
order: 5,
|
|
6969
7321
|
inject: (sessionId) => chatFace(sessionId)
|
|
6970
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");
|
|
6971
7329
|
/**
|
|
6972
7330
|
* The client runtime's session list store, read structurally. Used to find
|
|
6973
7331
|
* the session the composer is staged on and whether it runs the ACP preset —
|