@deepseek-harness-tui/dsh-tui 0.8.6 → 0.8.7
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.md +13 -3
- package/bin/dsh-tui.js +269 -218
- package/cordis.patch.yml +48 -22
- package/lib/types/cc/figures.d.ts.map +1 -1
- package/lib/types/cc/figures.js +14 -13
- package/lib/types/cc/syntaxRuns.d.ts +16 -0
- package/lib/types/cc/syntaxRuns.d.ts.map +1 -0
- package/lib/types/cc/syntaxRuns.js +79 -0
- package/lib/types/commands.d.ts +14 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +20 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts +19 -0
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -0
- package/lib/types/components/Chat/SubagentMessage.js +75 -0
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -1
- package/lib/types/components/EffortInputBorder.js +2 -2
- package/lib/types/components/EffortTierBadge.js +1 -1
- package/lib/types/components/HelpMenu.d.ts +8 -1
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +15 -4
- package/lib/types/components/LogoV2.d.ts +3 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +2 -2
- package/lib/types/components/MessageList.d.ts +6 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +17 -6
- package/lib/types/components/ModelPicker.js +2 -2
- package/lib/types/components/PromptInput.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +75 -15
- package/lib/types/components/SkillsPicker.js +2 -2
- package/lib/types/components/SplitDiffView.d.ts +1 -13
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +6 -105
- package/lib/types/components/SubagentCard.d.ts +9 -0
- package/lib/types/components/SubagentCard.d.ts.map +1 -0
- package/lib/types/components/SubagentCard.js +26 -0
- package/lib/types/components/SubagentDashboard.d.ts +13 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -0
- package/lib/types/components/SubagentDashboard.js +50 -0
- package/lib/types/components/SubagentDetailScene.d.ts +16 -0
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -0
- package/lib/types/components/SubagentDetailScene.js +141 -0
- package/lib/types/components/SyntaxText.d.ts +15 -0
- package/lib/types/components/SyntaxText.d.ts.map +1 -0
- package/lib/types/components/SyntaxText.js +34 -0
- package/lib/types/components/ThinkingToggle.d.ts +3 -10
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +4 -9
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +18 -7
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -0
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +42 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +15 -2
- package/lib/types/dsh-adapter/channel.d.ts +53 -2
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +294 -7
- package/lib/types/dsh-adapter/contract.d.ts +27 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +56 -10
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +9 -3
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +38 -0
- package/lib/types/dsh-adapter/shortcuts.js +3 -3
- package/lib/types/dsh-adapter/subagents.d.ts +87 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -0
- package/lib/types/dsh-adapter/subagents.js +204 -0
- package/lib/types/herdr.d.ts +28 -0
- package/lib/types/herdr.d.ts.map +1 -0
- package/lib/types/herdr.js +133 -0
- package/lib/types/i18n.d.ts +215 -31
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +65 -14
- package/lib/types/ink/ink.d.ts +4 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +46 -15
- package/lib/types/minimalMode.d.ts +13 -0
- package/lib/types/minimalMode.d.ts.map +1 -0
- package/lib/types/minimalMode.js +17 -0
- package/lib/types/screens/Chat.d.ts +1 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +94 -43
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +6 -2
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
- package/lib/types/screens/TrajectoryScene.js +4 -2
- package/lib/types/theme.d.ts +8 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +38 -14
- package/lib/types/tips.js +3 -3
- package/lib/types/update.d.ts +18 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +73 -2
- package/lib/types/utils/execFileNoThrow.d.ts +3 -20
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
- package/lib/types/utils/execFileNoThrow.js +53 -32
- package/lib/types/utils/externalEditor.d.ts +12 -11
- package/lib/types/utils/externalEditor.d.ts.map +1 -1
- package/lib/types/utils/externalEditor.js +11 -11
- package/package.json +64 -58
|
@@ -7,7 +7,7 @@ import { SessionId } from '@deepseek-ai/dsh-session';
|
|
|
7
7
|
import { renderContextSections, renderPrompt } from '@deepseek-ai/dsh-system-prompt';
|
|
8
8
|
import { loadBaselineInstructions } from '@deepseek-ai/dsh-agent-instructions';
|
|
9
9
|
import { extname, isAbsolute, join } from 'node:path';
|
|
10
|
-
import { completeCommands, isLocalCommandName, LOCAL_COMMANDS, parseCommandName } from '../commands.js';
|
|
10
|
+
import { completeCommands, HIDDEN_COMMAND_NAMES, isLocalCommandName, LOCAL_COMMANDS, parseCommandName } from '../commands.js';
|
|
11
11
|
import { clearResumeTarget, forgetSession, readResumeTarget, touchSession, writeResumeTarget } from '../sessionHistory.js';
|
|
12
12
|
import { appendSessionTitle, deleteSessionLog, ensureLegacySessionEventTypes, sessionsRoots } from './compat/index.js';
|
|
13
13
|
import { listSummaries, locateSession, noteBranch, previewSession, } from './sessions/index.js';
|
|
@@ -25,8 +25,10 @@ import { extractMentions } from '../utils/mentions.js';
|
|
|
25
25
|
import { t } from '../i18n.js';
|
|
26
26
|
import { modeDisplayName, resolveSessionModes } from '../sessionModes.js';
|
|
27
27
|
import { normalizeStatusBar, normalizeToolBackground } from '../tuiDisplayPrefs.js';
|
|
28
|
+
import { SubagentActivityStore } from './subagents.js';
|
|
28
29
|
import { ActivityTracker } from 'dsh-working-activity/status';
|
|
29
30
|
import { featureOn } from 'dsh-working-activity/config';
|
|
31
|
+
import { setMinimalMode } from '../minimalMode.js';
|
|
30
32
|
import { readActivityConfig } from '../activityPrefs.js';
|
|
31
33
|
import { attachSessionToWorkspace } from './workspace.js';
|
|
32
34
|
import { createLocalWorkspaceRuntime, getHostWorkspaceRuntime } from './workspaces.js';
|
|
@@ -40,6 +42,7 @@ import { installDecisionGuard } from './decision-guard.js';
|
|
|
40
42
|
import { commandOwner } from './command-attribution.js';
|
|
41
43
|
import { readGrantStore } from './grants.js';
|
|
42
44
|
import { hasCommandErrorCode, mapCommandError } from './command-errors.js';
|
|
45
|
+
import { installedLineOf } from './contract.js';
|
|
43
46
|
import { pluginsInfoLines } from './plugins-info.js';
|
|
44
47
|
import { cleanRenderText, cleanScalarText } from './sanitize.js';
|
|
45
48
|
/** `tui/input` return normalization: transform/handled/cancel or no opinion.
|
|
@@ -136,6 +139,20 @@ function normalizeRewindDoneSummary(result, warn) {
|
|
|
136
139
|
* observation (a provider whose directory watcher is still warming).
|
|
137
140
|
*/
|
|
138
141
|
const SKILL_COMMAND_RETRY_MS = 800;
|
|
142
|
+
/** Names the subagent delegation tools ship under (preset `toolName` values
|
|
143
|
+
* plus the CLI default); each renders as a live subagent card, never a plain
|
|
144
|
+
* tool card. */
|
|
145
|
+
const SUBAGENT_TOOL_NAMES = new Set([
|
|
146
|
+
'task',
|
|
147
|
+
'subagent',
|
|
148
|
+
'subagent_fork',
|
|
149
|
+
'subagent_claude_code',
|
|
150
|
+
'subagent_codex',
|
|
151
|
+
'spawn_task',
|
|
152
|
+
]);
|
|
153
|
+
function isSubagentToolName(name) {
|
|
154
|
+
return SUBAGENT_TOOL_NAMES.has(name.toLowerCase());
|
|
155
|
+
}
|
|
139
156
|
const ARGS_PREVIEW_LIMIT = 160;
|
|
140
157
|
const RESULT_PREVIEW_LIMIT = 240;
|
|
141
158
|
/** Local `!`-command output cap (mirrors the result preview limit). */
|
|
@@ -405,6 +422,26 @@ async function waitForTurnEnd(session, fromSeq, timeoutMs) {
|
|
|
405
422
|
export function createChannel(ctx, initialAgent, options) {
|
|
406
423
|
let agent = initialAgent;
|
|
407
424
|
let currentHandle = options.handle;
|
|
425
|
+
const subagentControl = {
|
|
426
|
+
interrupt(agentId) {
|
|
427
|
+
const child = subagentStore.get(agentId);
|
|
428
|
+
const target = child?.sessionId ?? agentId;
|
|
429
|
+
const runtime = ctx.subagents;
|
|
430
|
+
if (!runtime?.interrupt || !target)
|
|
431
|
+
return false;
|
|
432
|
+
try {
|
|
433
|
+
runtime.interrupt(target, { kind: 'ancestor', agent });
|
|
434
|
+
subagentStore.onCancelled(agentId, 'interrupted');
|
|
435
|
+
state.subagents = subagentStore.snapshot();
|
|
436
|
+
syncSubagentRows();
|
|
437
|
+
state.emit();
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
};
|
|
408
445
|
// D-7 backstop: the extensions row installs the decision-subscription
|
|
409
446
|
// gate, but the channel IS the dispatch path — a stale patch without that
|
|
410
447
|
// row (or a bare embed mounting neither) would otherwise leave tui/input
|
|
@@ -418,6 +455,55 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
418
455
|
const fallbackGrantStore = readGrantStore();
|
|
419
456
|
const currentGrantStore = () => ctx.get('tuiPluginHost')?.grants ?? fallbackGrantStore;
|
|
420
457
|
installDecisionGuard(ctx, currentGrantStore());
|
|
458
|
+
// Subagent activity tracking: collects agent/subagent/*, session/event for
|
|
459
|
+
// subagents, and exposes live snapshots for the UI.
|
|
460
|
+
const subagentStore = new SubagentActivityStore();
|
|
461
|
+
// Subagent ChatRow tracking: maps agentId to its ChatRow for live updates.
|
|
462
|
+
const subagentRowsByAgentId = new Map();
|
|
463
|
+
// Task tool descriptions, queued in call order; each subagent/start consumes
|
|
464
|
+
// the oldest one so the card shows the user-visible task label.
|
|
465
|
+
const pendingTaskDescriptions = [];
|
|
466
|
+
/**
|
|
467
|
+
* Sync subagentStore state into ChatRows (insert/update in state.rows).
|
|
468
|
+
* Called whenever subagent state changes (spawned/completed/failed/output).
|
|
469
|
+
*/
|
|
470
|
+
const syncSubagentRows = () => {
|
|
471
|
+
const snapshot = subagentStore.snapshot();
|
|
472
|
+
for (const sub of snapshot) {
|
|
473
|
+
let row = subagentRowsByAgentId.get(sub.agentId);
|
|
474
|
+
if (!row) {
|
|
475
|
+
// New subagent: insert a new ChatRow after the last user or assistant message
|
|
476
|
+
row = {
|
|
477
|
+
id: nextRowId++,
|
|
478
|
+
kind: 'subagent',
|
|
479
|
+
text: sub.description,
|
|
480
|
+
subagent: undefined, // will be filled below
|
|
481
|
+
};
|
|
482
|
+
subagentRowsByAgentId.set(sub.agentId, row);
|
|
483
|
+
state.rows.push(row);
|
|
484
|
+
}
|
|
485
|
+
const subagentRow = {
|
|
486
|
+
agentId: sub.agentId,
|
|
487
|
+
runId: sub.runId,
|
|
488
|
+
description: sub.description,
|
|
489
|
+
provider: sub.provider,
|
|
490
|
+
model: sub.model || 'default',
|
|
491
|
+
effort: sub.effort,
|
|
492
|
+
status: sub.status,
|
|
493
|
+
startedAt: sub.startedAt,
|
|
494
|
+
completedAt: sub.completedAt,
|
|
495
|
+
durationMs: sub.completedAt ? sub.completedAt - sub.startedAt : Date.now() - sub.startedAt,
|
|
496
|
+
outputLines: sub.output.slice(-3),
|
|
497
|
+
toolCalls: sub.toolCalls,
|
|
498
|
+
tokens: sub.tokens,
|
|
499
|
+
summary: sub.summary,
|
|
500
|
+
stopReason: sub.stopReason,
|
|
501
|
+
error: sub.error,
|
|
502
|
+
};
|
|
503
|
+
row.subagent = subagentRow;
|
|
504
|
+
row.text = sub.description;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
421
507
|
// The DSH slash-command registry (optional service): /plan, /goal and
|
|
422
508
|
// friends register here; the TUI merges their descriptors into the slash
|
|
423
509
|
// menu and dispatches through `execute` (which logs the paired
|
|
@@ -686,6 +772,12 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
686
772
|
/** Monotonic token: only the latest `interruptAndDeliver` re-queues, so a
|
|
687
773
|
* second interrupt while the abort settles cannot double-deliver. */
|
|
688
774
|
let interruptSeq = 0;
|
|
775
|
+
// Cancellation is asynchronous: a fast second Esc can arrive after the
|
|
776
|
+
// driver has accepted the first abort but before its turn/end event lands.
|
|
777
|
+
// Do not cancel the same driver twice, or the second cancel can swallow the
|
|
778
|
+
// replacement work queued by interruptAndDeliver and leave the UI gated on
|
|
779
|
+
// a working flag that has not observed turn/end yet.
|
|
780
|
+
let cancelInFlight = false;
|
|
689
781
|
/** The llm runtime seam (dsh-llm LlmRuntime): route metadata resolution. */
|
|
690
782
|
const llmRuntime = ctx.get('llm');
|
|
691
783
|
/** Mutable per-agent model selection (dsh-agent's routing override seam).
|
|
@@ -818,6 +910,16 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
818
910
|
applyEffort(found);
|
|
819
911
|
return true;
|
|
820
912
|
};
|
|
913
|
+
/** Whether the installed command service takes composer images: rc line
|
|
914
|
+
* gate with a structural fallback, so a failed manifest probe (bundlers,
|
|
915
|
+
* exotic loaders) still lands on the 4-param rc.8 shape at runtime. */
|
|
916
|
+
const commandServiceSupportsImages = (service) => {
|
|
917
|
+
const line = installedLineOf('@deepseek-ai/dsh-commands');
|
|
918
|
+
if (line !== undefined)
|
|
919
|
+
return line >= 8;
|
|
920
|
+
return typeof service.execute?.length === 'number'
|
|
921
|
+
&& service.execute.length >= 4;
|
|
922
|
+
};
|
|
821
923
|
/** Run one DSH registry command (`/plan`, …) on the live agent; the text
|
|
822
924
|
* of its result, '' when the result is textless, undefined when the
|
|
823
925
|
* command is not registered, and the error message when it throws. */
|
|
@@ -864,7 +966,22 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
864
966
|
return t('command-invoke-denied-owner', { name, owner: owner.componentId });
|
|
865
967
|
}
|
|
866
968
|
try {
|
|
867
|
-
const
|
|
969
|
+
const signal = new AbortController().signal;
|
|
970
|
+
const line = `/${name}${rawInput}`;
|
|
971
|
+
const images = await registryCommandImages(commandService, definition, line, signal);
|
|
972
|
+
// rc.8 moved the signal to the 4th parameter and added composer
|
|
973
|
+
// images; older lines (rc.7/rc.6) take (agent, line, signal).
|
|
974
|
+
const execution = images === undefined
|
|
975
|
+
? await commandService.execute(agent, line, signal)
|
|
976
|
+
: await commandService.execute(agent, line, images.images, signal);
|
|
977
|
+
if (images !== undefined && images.dropped.length > 0) {
|
|
978
|
+
// Loud-drop policy mirrors the submit pipeline (mentions-missing):
|
|
979
|
+
// a referenced image that never reached the command must be visible.
|
|
980
|
+
state.notify(t('mentions-missing', { paths: images.dropped.join(' ') }), {
|
|
981
|
+
color: 'warning',
|
|
982
|
+
timeoutMs: 4000,
|
|
983
|
+
});
|
|
984
|
+
}
|
|
868
985
|
// `undefined` = not registered; a handler error surfaces as its
|
|
869
986
|
// message so the user sees why the command failed.
|
|
870
987
|
return execution?.result.text ?? '';
|
|
@@ -873,6 +990,51 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
873
990
|
return error instanceof Error ? error.message : String(error);
|
|
874
991
|
}
|
|
875
992
|
};
|
|
993
|
+
/** Encode the staged `@`-mention images the user pasted for THIS command
|
|
994
|
+
* line into rc.8's `EncodedImageAttachment` payloads; undefined = the
|
|
995
|
+
* installed dsh-commands line predates composer images (rc.7/rc.6), so
|
|
996
|
+
* the caller uses the legacy 3-arg invoke. Matches the submit pipeline's
|
|
997
|
+
* token rule (expandMentions): a staged image attaches only when the
|
|
998
|
+
* line references its token. A command that does not declare
|
|
999
|
+
* `input.images` gets NO images — rc.8 admission settles such a batch
|
|
1000
|
+
* as an error, and upstream sends images only to image-capable commands.
|
|
1001
|
+
* A failing read drops just that image (reported via the returned
|
|
1002
|
+
* tokens) while the command still runs. */
|
|
1003
|
+
const registryCommandImages = async (service, definition, line, signal) => {
|
|
1004
|
+
if (!commandServiceSupportsImages(service))
|
|
1005
|
+
return undefined;
|
|
1006
|
+
const declaresImages = definition?.input?.images === true;
|
|
1007
|
+
if (!declaresImages || stagedImages.size === 0)
|
|
1008
|
+
return { images: [], dropped: [] };
|
|
1009
|
+
const store = mentionAttachments(ctx);
|
|
1010
|
+
if (typeof store?.readImage !== 'function')
|
|
1011
|
+
return { images: [], dropped: [] };
|
|
1012
|
+
const images = [];
|
|
1013
|
+
const dropped = [];
|
|
1014
|
+
for (const [token, attachment] of stagedImages) {
|
|
1015
|
+
if (!line.includes(token))
|
|
1016
|
+
continue;
|
|
1017
|
+
try {
|
|
1018
|
+
const stored = await store.readImage(attachment, signal);
|
|
1019
|
+
if (stored?.data instanceof Uint8Array && stored.data.byteLength > 0) {
|
|
1020
|
+
images.push({
|
|
1021
|
+
mediaType: attachment.mediaType,
|
|
1022
|
+
data: Buffer.from(stored.data).toString('base64'),
|
|
1023
|
+
name: attachment.name,
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
dropped.push(token);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
catch {
|
|
1031
|
+
// One unreadable staged image is dropped — same loud policy as the
|
|
1032
|
+
// submit pipeline's mentions-missing warning (deliverUserText).
|
|
1033
|
+
dropped.push(token);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return { images, dropped };
|
|
1037
|
+
};
|
|
876
1038
|
// Session-mode folds: last-wins projections over the session log. The
|
|
877
1039
|
// event types are registered by dsh-plan-mode / dsh-sandbox-policy /
|
|
878
1040
|
// dsh-user-approval and are NOT in this package's typed SessionEvent
|
|
@@ -1001,6 +1163,8 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1001
1163
|
thinkingFold: options.thinkingFold ?? 'preview',
|
|
1002
1164
|
toolBackground: normalizeToolBackground(options.toolBackground),
|
|
1003
1165
|
statusBar: normalizeStatusBar(options.statusBar),
|
|
1166
|
+
whale: options.whale !== false,
|
|
1167
|
+
minimal: options.minimal === true,
|
|
1004
1168
|
activityEnabled: options.activity !== false,
|
|
1005
1169
|
contextBarEnabled: options.contextBar !== false,
|
|
1006
1170
|
agentPreset: options.agentPreset,
|
|
@@ -1042,6 +1206,8 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1042
1206
|
thinking: 0,
|
|
1043
1207
|
tools: 0,
|
|
1044
1208
|
},
|
|
1209
|
+
subagents: [],
|
|
1210
|
+
subagentControl,
|
|
1045
1211
|
subscribe(listener) {
|
|
1046
1212
|
listeners.add(listener);
|
|
1047
1213
|
return () => {
|
|
@@ -1165,6 +1331,11 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1165
1331
|
cancel() {
|
|
1166
1332
|
// Keep the staged queue: an interrupt aborts the running turn but the
|
|
1167
1333
|
// queued/steered messages are delivered as the next turn (web parity).
|
|
1334
|
+
// Cancellation converges asynchronously; ignore a repeated Esc/Ctrl+C
|
|
1335
|
+
// until the aborted turn has produced its terminal event.
|
|
1336
|
+
if (cancelInFlight)
|
|
1337
|
+
return;
|
|
1338
|
+
cancelInFlight = true;
|
|
1168
1339
|
agent.cancel({ kind: 'user' }, { keepInbox: true });
|
|
1169
1340
|
},
|
|
1170
1341
|
interruptAndDeliver(texts) {
|
|
@@ -1176,8 +1347,13 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1176
1347
|
// followup. dsh-agent's cancel-convergence wake latch accepts this
|
|
1177
1348
|
// wake immediately after cancel and starts it once the aborted turn
|
|
1178
1349
|
// retires; waiting for whenIdle is unsafe because it also follows
|
|
1179
|
-
// replacement work and may never settle.
|
|
1180
|
-
|
|
1350
|
+
// replacement work and may never settle. If cancellation is already
|
|
1351
|
+
// in flight, keep the existing abort and still replace the pending
|
|
1352
|
+
// interrupt delivery; fake/embedded agents may not emit turn/end.
|
|
1353
|
+
if (!cancelInFlight) {
|
|
1354
|
+
cancelInFlight = true;
|
|
1355
|
+
agent.cancel({ kind: 'user' });
|
|
1356
|
+
}
|
|
1181
1357
|
const token = ++interruptSeq;
|
|
1182
1358
|
const deliver = () => {
|
|
1183
1359
|
// A second interrupt while the abort is still settling must not
|
|
@@ -1985,6 +2161,19 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
1985
2161
|
state.statusBar = next;
|
|
1986
2162
|
state.emit();
|
|
1987
2163
|
},
|
|
2164
|
+
setWhale(visible) {
|
|
2165
|
+
if (visible === state.whale)
|
|
2166
|
+
return;
|
|
2167
|
+
state.whale = visible;
|
|
2168
|
+
state.emit();
|
|
2169
|
+
},
|
|
2170
|
+
setMinimal(enabled) {
|
|
2171
|
+
setMinimalMode(enabled);
|
|
2172
|
+
if (enabled === state.minimal)
|
|
2173
|
+
return;
|
|
2174
|
+
state.minimal = enabled;
|
|
2175
|
+
state.emit();
|
|
2176
|
+
},
|
|
1988
2177
|
setActivityFrames(name) {
|
|
1989
2178
|
if (!isPresetName(name)) {
|
|
1990
2179
|
state.notify(t('unknown-activity-preset', { name }), { color: 'error' });
|
|
@@ -2098,13 +2287,15 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
2098
2287
|
// snapshot() over list(): only a COMPLETE observation is authoritative
|
|
2099
2288
|
// (same contract as the skill-command merge above) — a partial catalog
|
|
2100
2289
|
// must surface as "failed", not as a misleading near-empty picker.
|
|
2101
|
-
const
|
|
2290
|
+
const target = agent;
|
|
2291
|
+
const registry = skillRegistryFor(target);
|
|
2102
2292
|
if (registry === undefined)
|
|
2103
2293
|
return [];
|
|
2104
2294
|
try {
|
|
2105
|
-
const observation = await registry.snapshot(skillViewOptions(
|
|
2106
|
-
if (!observation.complete)
|
|
2295
|
+
const observation = await registry.snapshot(skillViewOptions(target));
|
|
2296
|
+
if (target !== agent || !observation.complete) {
|
|
2107
2297
|
return undefined;
|
|
2298
|
+
}
|
|
2108
2299
|
return observation.skills.map(skill => ({
|
|
2109
2300
|
name: skill.name,
|
|
2110
2301
|
description: skill.description,
|
|
@@ -2717,6 +2908,10 @@ export function createChannel(ctx, initialAgent, options) {
|
|
|
2717
2908
|
const merged = [...LOCAL_COMMANDS];
|
|
2718
2909
|
if (commandService) {
|
|
2719
2910
|
for (const descriptor of commandService.list(target)) {
|
|
2911
|
+
// Hidden TUI commands (e.g. /deepseek) stay out of the public
|
|
2912
|
+
// command catalog even if a plugin/skill happens to share the name.
|
|
2913
|
+
if (HIDDEN_COMMAND_NAMES.has(descriptor.name))
|
|
2914
|
+
continue;
|
|
2720
2915
|
if (merged.some(command => command.name === descriptor.name))
|
|
2721
2916
|
continue;
|
|
2722
2917
|
const descriptions = commandTrees?.descriptions(descriptor.name);
|
|
@@ -3559,6 +3754,22 @@ ${output}
|
|
|
3559
3754
|
// no card is a no-op below.
|
|
3560
3755
|
if (event.data.name === 'ask_user_question')
|
|
3561
3756
|
break;
|
|
3757
|
+
// The Task tool's plain card is replaced by the live subagent card
|
|
3758
|
+
// (Kimi Code semantics): the delegation itself renders as a subagent
|
|
3759
|
+
// row, so the raw args/result card would only duplicate it. The call
|
|
3760
|
+
// still runs - only its transcript rendering is suppressed.
|
|
3761
|
+
if (isSubagentToolName(event.data.name)) {
|
|
3762
|
+
try {
|
|
3763
|
+
const args = JSON.parse(event.data.arguments);
|
|
3764
|
+
if (typeof args.description === 'string' && args.description)
|
|
3765
|
+
pendingTaskDescriptions.push(args.description);
|
|
3766
|
+
}
|
|
3767
|
+
catch {
|
|
3768
|
+
// Unparseable args leave the queue untouched; the card falls back
|
|
3769
|
+
// to the provider label.
|
|
3770
|
+
}
|
|
3771
|
+
break;
|
|
3772
|
+
}
|
|
3562
3773
|
// Reasoning that led to a tool call is done thinking — fold the
|
|
3563
3774
|
// preview now, before the tool card grows the transcript past it
|
|
3564
3775
|
// (see foldLiveReasoning).
|
|
@@ -3629,6 +3840,7 @@ ${output}
|
|
|
3629
3840
|
break;
|
|
3630
3841
|
}
|
|
3631
3842
|
case 'turn/start': {
|
|
3843
|
+
cancelInFlight = false;
|
|
3632
3844
|
state.working = true;
|
|
3633
3845
|
state.turnStart = Date.now();
|
|
3634
3846
|
state.responseChars = 0;
|
|
@@ -3644,6 +3856,7 @@ ${output}
|
|
|
3644
3856
|
break;
|
|
3645
3857
|
}
|
|
3646
3858
|
case 'turn/end': {
|
|
3859
|
+
cancelInFlight = false;
|
|
3647
3860
|
settleStreaming();
|
|
3648
3861
|
state.working = false;
|
|
3649
3862
|
state.activeToolCount = 0;
|
|
@@ -3910,6 +4123,19 @@ ${output}
|
|
|
3910
4123
|
};
|
|
3911
4124
|
})(),
|
|
3912
4125
|
ctx.on('session/event', (session, event) => {
|
|
4126
|
+
// First check if this is a subagent session
|
|
4127
|
+
const subagentId = subagentStore.getSubagentIdBySession(session);
|
|
4128
|
+
if (subagentId) {
|
|
4129
|
+
subagentStore.onSessionEvent(subagentId, event);
|
|
4130
|
+
state.subagents = subagentStore.snapshot();
|
|
4131
|
+
syncSubagentRows();
|
|
4132
|
+
if (event.type === 'assistant/chunk')
|
|
4133
|
+
state.emitStream();
|
|
4134
|
+
else
|
|
4135
|
+
state.emit();
|
|
4136
|
+
return;
|
|
4137
|
+
}
|
|
4138
|
+
// Otherwise handle main agent session
|
|
3913
4139
|
if (session !== agent.session)
|
|
3914
4140
|
return;
|
|
3915
4141
|
// Observation broker (C-042): maps user/message + assistant/message
|
|
@@ -3940,6 +4166,67 @@ ${output}
|
|
|
3940
4166
|
else
|
|
3941
4167
|
state.emit();
|
|
3942
4168
|
}),
|
|
4169
|
+
// Subagent lifecycle tracking. The dsh-subagent service publishes scoped
|
|
4170
|
+
// observe-only events as `subagent/start` and `subagent/end`; the parent
|
|
4171
|
+
// Agent is carried by Cordis scope dispatch, not included in the payload.
|
|
4172
|
+
(() => {
|
|
4173
|
+
const disposeStart = ctx.on('subagent/start', (info) => {
|
|
4174
|
+
if (!info?.id)
|
|
4175
|
+
return;
|
|
4176
|
+
subagentStore.onSpawned(info.id, info.provider || 'subagent', info.provider, {
|
|
4177
|
+
runId: info.runId ?? info.id,
|
|
4178
|
+
local: info.local,
|
|
4179
|
+
description: pendingTaskDescriptions.shift() ?? `${info.provider || 'subagent'} task`,
|
|
4180
|
+
});
|
|
4181
|
+
// In-process providers publish a child Agent during this notification.
|
|
4182
|
+
// Resolve through ctx.get('agents') (the property proxy is
|
|
4183
|
+
// topology-sensitive); the child carries its session (live output
|
|
4184
|
+
// stream) and its provider/model route for the card header.
|
|
4185
|
+
try {
|
|
4186
|
+
const agents = ctx.get('agents');
|
|
4187
|
+
const child = agents?.get(info.id);
|
|
4188
|
+
if (child?.session) {
|
|
4189
|
+
subagentStore.linkSession(info.id, child.session);
|
|
4190
|
+
const model = child.options?.model ?? child.options?.provider;
|
|
4191
|
+
if (model)
|
|
4192
|
+
subagentStore.patch(info.id, { model, provider: child.options?.provider ?? info.provider });
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
catch {
|
|
4196
|
+
// Session discovery is best-effort and must not break the parent turn.
|
|
4197
|
+
}
|
|
4198
|
+
state.subagents = subagentStore.snapshot();
|
|
4199
|
+
syncSubagentRows();
|
|
4200
|
+
state.emit();
|
|
4201
|
+
});
|
|
4202
|
+
const disposeEnd = ctx.on('subagent/end', (info) => {
|
|
4203
|
+
if (!info?.id)
|
|
4204
|
+
return;
|
|
4205
|
+
const output = Array.isArray(info.lastAssistantMessage)
|
|
4206
|
+
? info.lastAssistantMessage
|
|
4207
|
+
.map(block => typeof block === 'object' && block !== null && 'text' in block ? String(block.text ?? '') : '')
|
|
4208
|
+
.filter(Boolean)
|
|
4209
|
+
.join('\n')
|
|
4210
|
+
: '';
|
|
4211
|
+
// The final assistant output becomes the card's summary only; the
|
|
4212
|
+
// running waterfall came from the child session stream, so echoing
|
|
4213
|
+
// it into the output buffer would duplicate it on the collapsed card.
|
|
4214
|
+
subagentStore.flushOutput(info.id);
|
|
4215
|
+
if (info.stopReason === 'completed')
|
|
4216
|
+
subagentStore.onCompleted(info.id, output, info.stopReason);
|
|
4217
|
+
else if (info.stopReason === 'cancelled' || info.stopReason === 'aborted')
|
|
4218
|
+
subagentStore.onCancelled(info.id, info.stopReason, output);
|
|
4219
|
+
else
|
|
4220
|
+
subagentStore.onFailed(info.id, info.stopReason || 'Unknown error');
|
|
4221
|
+
state.subagents = subagentStore.snapshot();
|
|
4222
|
+
syncSubagentRows();
|
|
4223
|
+
state.emit();
|
|
4224
|
+
});
|
|
4225
|
+
return () => {
|
|
4226
|
+
disposeStart();
|
|
4227
|
+
disposeEnd();
|
|
4228
|
+
};
|
|
4229
|
+
})(),
|
|
3943
4230
|
];
|
|
3944
4231
|
};
|
|
3945
4232
|
// Subagents inherit provider/model from AgentOptions, but resumed TUI
|
|
@@ -1,20 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
/** Primary validated upstream line (newest). */
|
|
2
|
+
export declare const UPSTREAM_VALIDATED_VERSION = "0.1.0-rc.8";
|
|
3
|
+
/** Every upstream rc line the adapter has been validated against.
|
|
4
|
+
*
|
|
5
|
+
* rc.8 = primary; rc.7 = previous line (full CI coverage); rc.6 = legacy
|
|
6
|
+
* line (install- and type-level compatibility, feature surface may lack
|
|
7
|
+
* rc.7/rc.8 additions — new features must degrade gracefully there).
|
|
8
|
+
* The peer range in package.json is deliberately wider than this list: an
|
|
9
|
+
* install on an older line is allowed but reports drift at boot.
|
|
10
|
+
*/
|
|
11
|
+
export declare const UPSTREAM_VALIDATED_RC_LINES: readonly [6, 7, 8];
|
|
2
12
|
/**
|
|
3
13
|
* Framework packages version on their own lines; the contract validates
|
|
4
14
|
* their MAJOR (breaking surface), not the harness rc number.
|
|
5
15
|
*/
|
|
6
16
|
export declare const UPSTREAM_FRAMEWORK_MAJORS: Record<string, number>;
|
|
7
17
|
/** Official packages the adapter consumes at runtime or as types. */
|
|
8
|
-
export declare const UPSTREAM_BLESSED_PACKAGES: readonly ["@deepseek-ai/cordis", "@deepseek-ai/schemastery", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-presets", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-cordis-host-runner", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-persona", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-storage", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-storage-json", "@deepseek-ai/dsh-workspace", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-terminal", "@deepseek-ai/dsh-terminal-bash", "@deepseek-ai/dsh-tool-ask-user", "@deepseek-ai/dsh-tool-bash-persistent", "@deepseek-ai/dsh-tool-cordis", "@deepseek-ai/dsh-user-approval", "@deepseek-ai/dsh-user-questions"];
|
|
18
|
+
export declare const UPSTREAM_BLESSED_PACKAGES: readonly ["@deepseek-ai/cordis", "@deepseek-ai/schemastery", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-presets", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-commands", "@deepseek-ai/dsh-cordis-host-runner", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-persona", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-storage", "@deepseek-ai/dsh-storage-domain", "@deepseek-ai/dsh-storage-json", "@deepseek-ai/dsh-workspace", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-terminal", "@deepseek-ai/dsh-terminal-bash", "@deepseek-ai/dsh-tool-ask-user", "@deepseek-ai/dsh-tool-bash-persistent", "@deepseek-ai/dsh-tool-cordis", "@deepseek-ai/dsh-user-approval", "@deepseek-ai/dsh-user-questions"];
|
|
9
19
|
export interface UpstreamDriftEntry {
|
|
10
20
|
package: string;
|
|
11
21
|
installed: string | undefined;
|
|
12
22
|
validated: string;
|
|
13
23
|
}
|
|
24
|
+
/** Human-readable summary of the validated lines, e.g. `0.1.0-rc.8 (rc.6/rc.7/rc.8)`. */
|
|
25
|
+
export declare const UPSTREAM_VALIDATED_LABEL: string;
|
|
14
26
|
export declare function installedUpstreamVersions(): Record<string, string | undefined>;
|
|
27
|
+
/** The installed upstream rc line of one blessed package (undefined when
|
|
28
|
+
* missing or not on the 0.1.0-rc line). Feature gates compare this against
|
|
29
|
+
* the line a behavior was introduced on, so the adapter degrades on older
|
|
30
|
+
* installs instead of calling APIs they do not have. */
|
|
31
|
+
export declare function installedLineOf(packageName: string): number | undefined;
|
|
32
|
+
/** The distinct upstream rc lines installed across the blessed harness
|
|
33
|
+
* packages (framework packages excluded). One line = coherent install;
|
|
34
|
+
* several = a mixed tree, which the per-package drift check cannot see.
|
|
35
|
+
* Empty when nothing (or no harness package) is installed. */
|
|
36
|
+
export declare function installedUpstreamLines(): number[];
|
|
15
37
|
/**
|
|
16
|
-
* Report every blessed package whose installed version is NOT the
|
|
17
|
-
* release
|
|
38
|
+
* Report every blessed package whose installed version is NOT one of the
|
|
39
|
+
* validated release lines. Empty array = the running install matches the
|
|
40
|
+
* contract.
|
|
18
41
|
*/
|
|
19
42
|
export declare function upstreamDrift(): UpstreamDriftEntry[];
|
|
20
43
|
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/contract.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/contract.ts"],"names":[],"mappings":"AAgBA,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,eAAe,CAAA;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,oBAAqB,CAAA;AAE7D;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG5D,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,2zBA2B5B,CAAA;AAEV,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,yFAAyF;AACzF,eAAO,MAAM,wBAAwB,QAAmG,CAAA;AAYxI,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAqB9E;AAED;;;yDAGyD;AACzD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAOD;;;+DAG+D;AAC/D,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAQjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,kBAAkB,EAAE,CAuBpD"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Upstream compatibility contract.
|
|
3
3
|
*
|
|
4
|
-
* The TUI is validated against
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The TUI is validated against a set of upstream release lines — the
|
|
5
|
+
* current primary (0.1.0-rc.8) plus older lines kept in backward
|
|
6
|
+
* compatibility (0.1.0-rc.7, 0.1.0-rc.6). Every official package this
|
|
7
|
+
* adapter touches is blessed here; anything else must go through upstream
|
|
8
|
+
* channels or the adapter, never the UI.
|
|
7
9
|
*
|
|
8
10
|
* `upstreamDrift()` powers both the boot-time warning (dev visibility) and
|
|
9
11
|
* the CI gate (scripts/verify-upstream-contract.ts) so a mismatched
|
|
@@ -11,7 +13,17 @@
|
|
|
11
13
|
*/
|
|
12
14
|
import { readFileSync } from 'node:fs';
|
|
13
15
|
import { fileURLToPath } from 'node:url';
|
|
14
|
-
|
|
16
|
+
/** Primary validated upstream line (newest). */
|
|
17
|
+
export const UPSTREAM_VALIDATED_VERSION = '0.1.0-rc.8';
|
|
18
|
+
/** Every upstream rc line the adapter has been validated against.
|
|
19
|
+
*
|
|
20
|
+
* rc.8 = primary; rc.7 = previous line (full CI coverage); rc.6 = legacy
|
|
21
|
+
* line (install- and type-level compatibility, feature surface may lack
|
|
22
|
+
* rc.7/rc.8 additions — new features must degrade gracefully there).
|
|
23
|
+
* The peer range in package.json is deliberately wider than this list: an
|
|
24
|
+
* install on an older line is allowed but reports drift at boot.
|
|
25
|
+
*/
|
|
26
|
+
export const UPSTREAM_VALIDATED_RC_LINES = [6, 7, 8];
|
|
15
27
|
/**
|
|
16
28
|
* Framework packages version on their own lines; the contract validates
|
|
17
29
|
* their MAJOR (breaking surface), not the harness rc number.
|
|
@@ -34,6 +46,7 @@ export const UPSTREAM_BLESSED_PACKAGES = [
|
|
|
34
46
|
'@deepseek-ai/dsh-llm',
|
|
35
47
|
'@deepseek-ai/dsh-persona',
|
|
36
48
|
'@deepseek-ai/dsh-session',
|
|
49
|
+
'@deepseek-ai/dsh-settings',
|
|
37
50
|
'@deepseek-ai/dsh-skill',
|
|
38
51
|
'@deepseek-ai/dsh-storage',
|
|
39
52
|
'@deepseek-ai/dsh-storage-domain',
|
|
@@ -48,6 +61,8 @@ export const UPSTREAM_BLESSED_PACKAGES = [
|
|
|
48
61
|
'@deepseek-ai/dsh-user-approval',
|
|
49
62
|
'@deepseek-ai/dsh-user-questions',
|
|
50
63
|
];
|
|
64
|
+
/** Human-readable summary of the validated lines, e.g. `0.1.0-rc.8 (rc.6/rc.7/rc.8)`. */
|
|
65
|
+
export const UPSTREAM_VALIDATED_LABEL = `${UPSTREAM_VALIDATED_VERSION} (${UPSTREAM_VALIDATED_RC_LINES.map((n) => `rc.${n}`).join('/')})`;
|
|
51
66
|
function resolvePackageJson(packageName) {
|
|
52
67
|
try {
|
|
53
68
|
const path = import.meta.resolve(`${packageName}/package.json`);
|
|
@@ -57,7 +72,13 @@ function resolvePackageJson(packageName) {
|
|
|
57
72
|
return undefined;
|
|
58
73
|
}
|
|
59
74
|
}
|
|
75
|
+
let cachedVersions;
|
|
60
76
|
export function installedUpstreamVersions() {
|
|
77
|
+
// Package manifests do not change mid-process; memoize so per-call gates
|
|
78
|
+
// (e.g. the command-images line check) stay cheap. Frozen so callers can
|
|
79
|
+
// never corrupt the shared cache.
|
|
80
|
+
if (cachedVersions !== undefined)
|
|
81
|
+
return cachedVersions;
|
|
61
82
|
const result = {};
|
|
62
83
|
for (const packageName of UPSTREAM_BLESSED_PACKAGES) {
|
|
63
84
|
let version;
|
|
@@ -73,15 +94,39 @@ export function installedUpstreamVersions() {
|
|
|
73
94
|
}
|
|
74
95
|
result[packageName] = version;
|
|
75
96
|
}
|
|
76
|
-
|
|
97
|
+
cachedVersions = Object.freeze(result);
|
|
98
|
+
return cachedVersions;
|
|
99
|
+
}
|
|
100
|
+
/** The installed upstream rc line of one blessed package (undefined when
|
|
101
|
+
* missing or not on the 0.1.0-rc line). Feature gates compare this against
|
|
102
|
+
* the line a behavior was introduced on, so the adapter degrades on older
|
|
103
|
+
* installs instead of calling APIs they do not have. */
|
|
104
|
+
export function installedLineOf(packageName) {
|
|
105
|
+
return rcNumber(installedUpstreamVersions()[packageName]);
|
|
77
106
|
}
|
|
78
107
|
function rcNumber(version) {
|
|
79
|
-
const match =
|
|
108
|
+
const match = /^0\.1\.0-rc\.(\d+)$/u.exec(version ?? '');
|
|
80
109
|
return match === null ? undefined : Number(match[1]);
|
|
81
110
|
}
|
|
111
|
+
/** The distinct upstream rc lines installed across the blessed harness
|
|
112
|
+
* packages (framework packages excluded). One line = coherent install;
|
|
113
|
+
* several = a mixed tree, which the per-package drift check cannot see.
|
|
114
|
+
* Empty when nothing (or no harness package) is installed. */
|
|
115
|
+
export function installedUpstreamLines() {
|
|
116
|
+
const lines = new Set();
|
|
117
|
+
for (const packageName of UPSTREAM_BLESSED_PACKAGES) {
|
|
118
|
+
if (UPSTREAM_FRAMEWORK_MAJORS[packageName] !== undefined)
|
|
119
|
+
continue;
|
|
120
|
+
const line = rcNumber(installedUpstreamVersions()[packageName]);
|
|
121
|
+
if (line !== undefined)
|
|
122
|
+
lines.add(line);
|
|
123
|
+
}
|
|
124
|
+
return [...lines].sort((a, b) => a - b);
|
|
125
|
+
}
|
|
82
126
|
/**
|
|
83
|
-
* Report every blessed package whose installed version is NOT the
|
|
84
|
-
* release
|
|
127
|
+
* Report every blessed package whose installed version is NOT one of the
|
|
128
|
+
* validated release lines. Empty array = the running install matches the
|
|
129
|
+
* contract.
|
|
85
130
|
*/
|
|
86
131
|
export function upstreamDrift() {
|
|
87
132
|
const drift = [];
|
|
@@ -96,13 +141,14 @@ export function upstreamDrift() {
|
|
|
96
141
|
matches = installedMajor === frameworkMajor;
|
|
97
142
|
}
|
|
98
143
|
else {
|
|
99
|
-
|
|
144
|
+
const installedLine = rcNumber(installed);
|
|
145
|
+
matches = installedLine !== undefined && UPSTREAM_VALIDATED_RC_LINES.includes(installedLine);
|
|
100
146
|
}
|
|
101
147
|
if (!matches) {
|
|
102
148
|
drift.push({
|
|
103
149
|
package: packageName,
|
|
104
150
|
installed,
|
|
105
|
-
validated: frameworkMajor !== undefined ? `major ${frameworkMajor}` :
|
|
151
|
+
validated: frameworkMajor !== undefined ? `major ${frameworkMajor}` : UPSTREAM_VALIDATED_LABEL,
|
|
106
152
|
});
|
|
107
153
|
}
|
|
108
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAErG,eAAO,MAAM,IAAI,YAAY,CAAA;AAU7B,eAAO,MAAM,MAAM,UAAa,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;yEAKqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;gDAC4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;gFAE4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;sBAIkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;4BAGwB;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;kFAC8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;gCAG4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;6DAEyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC;;8EAE0E;IAC1E,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC,sEAAsE;IACtE,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;IACpC;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA8ChC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAErG,eAAO,MAAM,IAAI,YAAY,CAAA;AAU7B,eAAO,MAAM,MAAM,UAAa,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;yEAKqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;gDAC4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;gFAE4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;sBAIkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;4BAGwB;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;kFAC8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;gCAG4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;6DAEyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC;;8EAE0E;IAC1E,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC,sEAAsE;IACtE,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;IACpC;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA8ChC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBvE"}
|