@aragon-agent/cli 0.6.38 → 0.6.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +263 -0
- package/README.md +544 -11
- package/dist/agent/controller.d.ts +245 -10
- package/dist/agent/controller.js +367 -22
- package/dist/agent/model-profile-settings.js +12 -1
- package/dist/agent/plan-prompt.d.ts +1 -3
- package/dist/agent/plan-prompt.js +13 -7
- package/dist/agent/reducer.d.ts +70 -2
- package/dist/agent/reducer.js +59 -6
- package/dist/agent/skill-triage-prompt.d.ts +42 -0
- package/dist/agent/skill-triage-prompt.js +74 -0
- package/dist/agent/system-prompt.d.ts +28 -2
- package/dist/agent/system-prompt.js +12 -4
- package/dist/cli.js +106 -32
- package/dist/commands/builtins.js +24 -3
- package/dist/commands/keys-command.d.ts +23 -0
- package/dist/commands/keys-command.js +38 -0
- package/dist/commands/registry.d.ts +39 -0
- package/dist/commands/skills.js +86 -1
- package/dist/commands/subagent-command.d.ts +25 -0
- package/dist/commands/subagent-command.js +242 -0
- package/dist/commands/task-command.d.ts +39 -0
- package/dist/commands/task-command.js +102 -0
- package/dist/compaction/command.js +16 -3
- package/dist/compaction/compactor.d.ts +25 -0
- package/dist/compaction/compactor.js +111 -10
- package/dist/compaction/digest.d.ts +24 -1
- package/dist/compaction/digest.js +139 -17
- package/dist/compaction/limits.d.ts +21 -0
- package/dist/compaction/limits.js +21 -0
- package/dist/compaction/reason-text.d.ts +36 -0
- package/dist/compaction/reason-text.js +78 -0
- package/dist/compaction/types.d.ts +82 -14
- package/dist/compaction/wiring.d.ts +13 -8
- package/dist/compaction/wiring.js +1 -0
- package/dist/config/cli-commands.d.ts +23 -0
- package/dist/config/cli-commands.js +36 -0
- package/dist/config/env.js +17 -4
- package/dist/config/load.js +55 -3
- package/dist/config/schema.d.ts +125 -10
- package/dist/config/schema.js +138 -14
- package/dist/config/store.js +23 -3
- package/dist/diagnostics/doctor.d.ts +9 -0
- package/dist/diagnostics/doctor.js +34 -0
- package/dist/diagnostics/info.d.ts +71 -0
- package/dist/diagnostics/info.js +91 -5
- package/dist/exec/bridge/caller-tools.d.ts +96 -0
- package/dist/exec/bridge/caller-tools.js +213 -0
- package/dist/exec/bridge/decisions.d.ts +78 -0
- package/dist/exec/bridge/decisions.js +116 -0
- package/dist/exec/bridge/human-answers.d.ts +38 -0
- package/dist/exec/bridge/human-answers.js +164 -0
- package/dist/exec/bridge/limits.d.ts +68 -0
- package/dist/exec/bridge/limits.js +68 -0
- package/dist/exec/bridge/ndjson.d.ts +17 -0
- package/dist/exec/bridge/ndjson.js +83 -0
- package/dist/exec/bridge/pending.d.ts +70 -0
- package/dist/exec/bridge/pending.js +132 -0
- package/dist/exec/bridge/protocol.d.ts +346 -0
- package/dist/exec/bridge/protocol.js +256 -0
- package/dist/exec/bridge/server.d.ts +142 -0
- package/dist/exec/bridge/server.js +502 -0
- package/dist/exec/bridge/source.d.ts +58 -0
- package/dist/exec/bridge/source.js +145 -0
- package/dist/exec/bridge/writer.d.ts +26 -0
- package/dist/exec/bridge/writer.js +156 -0
- package/dist/exec/cli-commands.js +30 -2
- package/dist/exec/emitter.d.ts +16 -2
- package/dist/exec/emitter.js +26 -6
- package/dist/exec/events.d.ts +465 -10
- package/dist/exec/events.js +47 -1
- package/dist/exec/hooks/dispatch.d.ts +76 -0
- package/dist/exec/hooks/dispatch.js +169 -0
- package/dist/exec/hooks/index.d.ts +83 -0
- package/dist/exec/hooks/index.js +205 -0
- package/dist/exec/hooks/limits.d.ts +56 -0
- package/dist/exec/hooks/limits.js +49 -0
- package/dist/exec/hooks/registry.d.ts +49 -0
- package/dist/exec/hooks/registry.js +91 -0
- package/dist/exec/hooks/tool-wrapper.d.ts +94 -0
- package/dist/exec/hooks/tool-wrapper.js +154 -0
- package/dist/exec/hooks/turn.d.ts +61 -0
- package/dist/exec/hooks/turn.js +59 -0
- package/dist/exec/hooks/validate.d.ts +85 -0
- package/dist/exec/hooks/validate.js +310 -0
- package/dist/exec/index.d.ts +130 -4
- package/dist/exec/index.js +501 -10
- package/dist/exec/options.d.ts +124 -2
- package/dist/exec/options.js +413 -6
- package/dist/exec/permission.d.ts +9 -1
- package/dist/exec/permission.js +7 -1
- package/dist/exec/request/limits.d.ts +51 -0
- package/dist/exec/request/limits.js +49 -0
- package/dist/exec/request/scope.d.ts +106 -0
- package/dist/exec/request/scope.js +149 -0
- package/dist/exec/result/limits.d.ts +74 -0
- package/dist/exec/result/limits.js +72 -0
- package/dist/exec/result/prompt.d.ts +28 -0
- package/dist/exec/result/prompt.js +74 -0
- package/dist/exec/result/schema.d.ts +99 -0
- package/dist/exec/result/schema.js +464 -0
- package/dist/exec/result/sink.d.ts +56 -0
- package/dist/exec/result/sink.js +50 -0
- package/dist/exec/result/tool.d.ts +37 -0
- package/dist/exec/result/tool.js +118 -0
- package/dist/exec/runner.d.ts +395 -1
- package/dist/exec/runner.js +860 -8
- package/dist/exec/threads/aggregate.d.ts +34 -0
- package/dist/exec/threads/aggregate.js +30 -0
- package/dist/exec/threads/capacity.d.ts +23 -0
- package/dist/exec/threads/capacity.js +38 -0
- package/dist/exec/threads/dispatch-source.d.ts +60 -0
- package/dist/exec/threads/dispatch-source.js +106 -0
- package/dist/exec/threads/execute-threads.d.ts +53 -0
- package/dist/exec/threads/execute-threads.js +234 -0
- package/dist/exec/threads/gate.d.ts +42 -0
- package/dist/exec/threads/gate.js +81 -0
- package/dist/exec/threads/identity.d.ts +36 -0
- package/dist/exec/threads/identity.js +67 -0
- package/dist/exec/threads/interrupt.d.ts +29 -0
- package/dist/exec/threads/interrupt.js +79 -0
- package/dist/exec/threads/limits.d.ts +58 -0
- package/dist/exec/threads/limits.js +35 -0
- package/dist/exec/threads/outcome.d.ts +47 -0
- package/dist/exec/threads/outcome.js +26 -0
- package/dist/exec/threads/registry.d.ts +81 -0
- package/dist/exec/threads/registry.js +427 -0
- package/dist/exec/threads/runtime.d.ts +107 -0
- package/dist/exec/threads/runtime.js +523 -0
- package/dist/exec/threads/thread-source.d.ts +20 -0
- package/dist/exec/threads/thread-source.js +134 -0
- package/dist/fast/limits.d.ts +54 -0
- package/dist/fast/limits.js +54 -0
- package/dist/fast/summary-call.d.ts +72 -0
- package/dist/fast/summary-call.js +133 -0
- package/dist/fast/summary-prompt.d.ts +106 -0
- package/dist/fast/summary-prompt.js +320 -0
- package/dist/fast/task-summary-types.d.ts +66 -0
- package/dist/fast/task-summary-types.js +17 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/proc/limits.d.ts +7 -0
- package/dist/proc/limits.js +7 -0
- package/dist/proc/log-mirror.d.ts +21 -4
- package/dist/proc/log-mirror.js +29 -7
- package/dist/proc/output-streams.d.ts +50 -0
- package/dist/proc/output-streams.js +54 -0
- package/dist/skills/service.d.ts +41 -0
- package/dist/skills/service.js +62 -0
- package/dist/skills/triage.d.ts +194 -0
- package/dist/skills/triage.js +393 -0
- package/dist/team/forced-dispatch.d.ts +95 -0
- package/dist/team/forced-dispatch.js +208 -0
- package/dist/team/limits.d.ts +21 -1
- package/dist/team/limits.js +21 -1
- package/dist/team/normalize.d.ts +15 -0
- package/dist/team/normalize.js +21 -4
- package/dist/team/prompt.d.ts +6 -3
- package/dist/team/prompt.js +9 -5
- package/dist/team/runtime.d.ts +9 -0
- package/dist/team/runtime.js +8 -0
- package/dist/team/subagent.d.ts +15 -0
- package/dist/team/subagent.js +45 -1
- package/dist/team/task-tool.js +11 -7
- package/dist/tools/bash-tool.d.ts +10 -0
- package/dist/tools/bash-tool.js +71 -6
- package/dist/tools/human-input.d.ts +35 -2
- package/dist/tools/human-input.js +38 -0
- package/dist/tools/index.d.ts +93 -1
- package/dist/tools/index.js +112 -3
- package/dist/tools/plan-tools.d.ts +20 -12
- package/dist/tools/plan-tools.js +24 -17
- package/dist/tools/tool-gate.d.ts +100 -0
- package/dist/tools/tool-gate.js +87 -0
- package/dist/ui/App.js +238 -12
- package/dist/ui/Header.d.ts +28 -16
- package/dist/ui/Header.js +21 -19
- package/dist/ui/StatusBar.d.ts +1 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/entries/CompactionCard.d.ts +11 -1
- package/dist/ui/entries/CompactionCard.js +65 -4
- package/dist/ui/interaction-hints.js +6 -0
- package/dist/ui/layout/OverlayFrame.d.ts +18 -0
- package/dist/ui/layout/OverlayFrame.js +19 -4
- package/dist/ui/layout/header-hints.d.ts +55 -62
- package/dist/ui/layout/header-hints.js +59 -166
- package/dist/ui/layout/header-left.d.ts +115 -0
- package/dist/ui/layout/header-left.js +206 -0
- package/dist/ui/layout/shortcut-panel-layout.d.ts +191 -0
- package/dist/ui/layout/shortcut-panel-layout.js +321 -0
- package/dist/ui/layout/status-detail-layout.js +5 -1
- package/dist/ui/layout/status-layout.d.ts +10 -0
- package/dist/ui/layout/status-layout.js +18 -1
- package/dist/ui/layout/task-summary-cell.d.ts +61 -0
- package/dist/ui/layout/task-summary-cell.js +65 -0
- package/dist/ui/overlays/HelpOverlay.d.ts +12 -0
- package/dist/ui/overlays/HelpOverlay.js +29 -2
- package/dist/ui/overlays/SettingsScreen.js +29 -5
- package/dist/ui/overlays/ShortcutPanel.d.ts +39 -0
- package/dist/ui/overlays/ShortcutPanel.js +106 -0
- package/dist/ui/overlays/TeamFanoutPage.d.ts +35 -0
- package/dist/ui/overlays/TeamFanoutPage.js +78 -0
- package/dist/ui/overlays/model-profile-state.d.ts +19 -1
- package/dist/ui/overlays/model-profile-state.js +12 -0
- package/dist/ui/overlays/team-fanout-state.d.ts +73 -0
- package/dist/ui/overlays/team-fanout-state.js +97 -0
- package/dist/ui/shortcuts.d.ts +81 -0
- package/dist/ui/shortcuts.js +221 -0
- package/package.json +2 -2
package/dist/fast/limits.d.ts
CHANGED
|
@@ -96,3 +96,57 @@ export declare const FAST_LIMITS: {
|
|
|
96
96
|
* exactly what `TEAM_BLOCK_VERSION` and `TODO_BLOCK_VERSION` are for.
|
|
97
97
|
*/
|
|
98
98
|
export declare const FAST_BLOCK_VERSION = "v2-2026-10";
|
|
99
|
+
/**
|
|
100
|
+
* The bounds one task-summary label may consume
|
|
101
|
+
* (tui-shortcut-drawer-and-task-summary D-11).
|
|
102
|
+
*
|
|
103
|
+
* STRUCTURAL, NOT POLICY, for the reason this file's header records: these say
|
|
104
|
+
* what a one-line header label can PHYSICALLY carry, not what the user prefers.
|
|
105
|
+
* The two user switches (`taskSummary.enabled` / `.fallback`) live in
|
|
106
|
+
* `config/schema.ts` and are not interchangeable with these.
|
|
107
|
+
*
|
|
108
|
+
* THE THREE CAPS HAVE THREE DIFFERENT JOBS, and v1 left that implicit (P1-8):
|
|
109
|
+
*
|
|
110
|
+
* - GENERATION (the system prompt in `summary-prompt.ts`): at most 6 words in
|
|
111
|
+
* space-separated languages, at most 12 characters in CJK. The normal path is
|
|
112
|
+
* constrained by this and by nothing else.
|
|
113
|
+
* - NORMALISATION (`normalizeTaskSummary`): `summaryMaxWords` and
|
|
114
|
+
* `cjkMaxGraphemes` are applied first, then `summaryMaxChars` catches the one
|
|
115
|
+
* case those two cannot - a long run with no whitespace. That order is
|
|
116
|
+
* asserted by `task-summary-normalize.test.ts`.
|
|
117
|
+
* - RENDERING: `maxCells` is the hard ceiling on the header cell. The header
|
|
118
|
+
* fits within whatever width is left; the drawer WRAPS instead of cutting.
|
|
119
|
+
*/
|
|
120
|
+
export declare const TASK_SUMMARY_LIMITS: {
|
|
121
|
+
/** Hard cap on the call's own output. A label is not a paragraph. */
|
|
122
|
+
readonly outputTokens: 64;
|
|
123
|
+
/**
|
|
124
|
+
* Wall-clock budget for ONE summary call, INCLUDING the fast registry's single
|
|
125
|
+
* retry. Short on purpose: the label is decoration on the header row, and a
|
|
126
|
+
* user waiting for the first token must never wait on it - the call is fired
|
|
127
|
+
* after `prompt()` has been started and nothing awaits it (AC-13).
|
|
128
|
+
*/
|
|
129
|
+
readonly timeoutMs: 8000;
|
|
130
|
+
/** How much of the user's message is sent. Long prompts are summarised from the head. */
|
|
131
|
+
readonly anchorMaxChars: 2000;
|
|
132
|
+
/** Storage ceiling. Only ever reached by a model that ignored the prompt rules. */
|
|
133
|
+
readonly summaryMaxChars: 60;
|
|
134
|
+
/** Space-separated languages: the label is at most this many words. */
|
|
135
|
+
readonly summaryMaxWords: 8;
|
|
136
|
+
/** CJK: the label is at most this many grapheme clusters. */
|
|
137
|
+
readonly cjkMaxGraphemes: 12;
|
|
138
|
+
/**
|
|
139
|
+
* The header cell's hard ceiling in terminal COLUMNS (not characters).
|
|
140
|
+
*
|
|
141
|
+
* Shared by `ui/layout/header-left.ts` and the drawer so the two cannot
|
|
142
|
+
* disagree about how wide a label is allowed to be. v1 referenced this
|
|
143
|
+
* constant without defining it (P1-8).
|
|
144
|
+
*/
|
|
145
|
+
readonly maxCells: 28;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Bumped whenever the task-summary system prompt's wording changes, so a
|
|
149
|
+
* behaviour report can be tied to a revision with one grep - the same purpose
|
|
150
|
+
* `FAST_BLOCK_VERSION` serves for the review prompt.
|
|
151
|
+
*/
|
|
152
|
+
export declare const TASK_SUMMARY_PROMPT_VERSION = "v1-2026-10";
|
package/dist/fast/limits.js
CHANGED
|
@@ -96,3 +96,57 @@ export const FAST_LIMITS = {
|
|
|
96
96
|
* exactly what `TEAM_BLOCK_VERSION` and `TODO_BLOCK_VERSION` are for.
|
|
97
97
|
*/
|
|
98
98
|
export const FAST_BLOCK_VERSION = 'v2-2026-10';
|
|
99
|
+
/**
|
|
100
|
+
* The bounds one task-summary label may consume
|
|
101
|
+
* (tui-shortcut-drawer-and-task-summary D-11).
|
|
102
|
+
*
|
|
103
|
+
* STRUCTURAL, NOT POLICY, for the reason this file's header records: these say
|
|
104
|
+
* what a one-line header label can PHYSICALLY carry, not what the user prefers.
|
|
105
|
+
* The two user switches (`taskSummary.enabled` / `.fallback`) live in
|
|
106
|
+
* `config/schema.ts` and are not interchangeable with these.
|
|
107
|
+
*
|
|
108
|
+
* THE THREE CAPS HAVE THREE DIFFERENT JOBS, and v1 left that implicit (P1-8):
|
|
109
|
+
*
|
|
110
|
+
* - GENERATION (the system prompt in `summary-prompt.ts`): at most 6 words in
|
|
111
|
+
* space-separated languages, at most 12 characters in CJK. The normal path is
|
|
112
|
+
* constrained by this and by nothing else.
|
|
113
|
+
* - NORMALISATION (`normalizeTaskSummary`): `summaryMaxWords` and
|
|
114
|
+
* `cjkMaxGraphemes` are applied first, then `summaryMaxChars` catches the one
|
|
115
|
+
* case those two cannot - a long run with no whitespace. That order is
|
|
116
|
+
* asserted by `task-summary-normalize.test.ts`.
|
|
117
|
+
* - RENDERING: `maxCells` is the hard ceiling on the header cell. The header
|
|
118
|
+
* fits within whatever width is left; the drawer WRAPS instead of cutting.
|
|
119
|
+
*/
|
|
120
|
+
export const TASK_SUMMARY_LIMITS = {
|
|
121
|
+
/** Hard cap on the call's own output. A label is not a paragraph. */
|
|
122
|
+
outputTokens: 64,
|
|
123
|
+
/**
|
|
124
|
+
* Wall-clock budget for ONE summary call, INCLUDING the fast registry's single
|
|
125
|
+
* retry. Short on purpose: the label is decoration on the header row, and a
|
|
126
|
+
* user waiting for the first token must never wait on it - the call is fired
|
|
127
|
+
* after `prompt()` has been started and nothing awaits it (AC-13).
|
|
128
|
+
*/
|
|
129
|
+
timeoutMs: 8_000,
|
|
130
|
+
/** How much of the user's message is sent. Long prompts are summarised from the head. */
|
|
131
|
+
anchorMaxChars: 2_000,
|
|
132
|
+
/** Storage ceiling. Only ever reached by a model that ignored the prompt rules. */
|
|
133
|
+
summaryMaxChars: 60,
|
|
134
|
+
/** Space-separated languages: the label is at most this many words. */
|
|
135
|
+
summaryMaxWords: 8,
|
|
136
|
+
/** CJK: the label is at most this many grapheme clusters. */
|
|
137
|
+
cjkMaxGraphemes: 12,
|
|
138
|
+
/**
|
|
139
|
+
* The header cell's hard ceiling in terminal COLUMNS (not characters).
|
|
140
|
+
*
|
|
141
|
+
* Shared by `ui/layout/header-left.ts` and the drawer so the two cannot
|
|
142
|
+
* disagree about how wide a label is allowed to be. v1 referenced this
|
|
143
|
+
* constant without defining it (P1-8).
|
|
144
|
+
*/
|
|
145
|
+
maxCells: 28,
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Bumped whenever the task-summary system prompt's wording changes, so a
|
|
149
|
+
* behaviour report can be tied to a revision with one grep - the same purpose
|
|
150
|
+
* `FAST_BLOCK_VERSION` serves for the review prompt.
|
|
151
|
+
*/
|
|
152
|
+
export const TASK_SUMMARY_PROMPT_VERSION = 'v1-2026-10';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bounded task-summary call (tui-shortcut-drawer-and-task-summary §6.2).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/fast/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* THIS MODULE OWNS ITS TIMER AND NOT ITS `AbortController`, which is the same
|
|
7
|
+
* prohibition `review-call.ts:7-29` states at length: a controller encapsulated
|
|
8
|
+
* with the call it aborts cannot be stamped by the lifecycle, so a cancellation
|
|
9
|
+
* and a timeout arrive as byte-identical bare errors. Here the consequence is
|
|
10
|
+
* cheaper (a label is decoration) but the rule is the same - the CALLER creates
|
|
11
|
+
* the controller, passes the signal, and aborts it once this function returns.
|
|
12
|
+
* `AgentController.summarizeTask` does exactly that, and it does so
|
|
13
|
+
* unconditionally, so a timed-out request never keeps a socket open.
|
|
14
|
+
*
|
|
15
|
+
* IT NEVER REJECTS. Every failure - four tier-off reasons, a missing key, a
|
|
16
|
+
* timeout, an abort, a provider error, an unusable reply - becomes a
|
|
17
|
+
* `TaskSummaryOutcome` carrying a `TaskSummaryReason`. A header label must not be
|
|
18
|
+
* able to produce an unhandled rejection in a TUI that has nowhere to show one.
|
|
19
|
+
*/
|
|
20
|
+
import type { AssistantMessage, LLMRequest } from '@aragon-agent/core';
|
|
21
|
+
import type { ModelRole } from '../config/model-profiles.js';
|
|
22
|
+
import type { CliConfig } from '../config/schema.js';
|
|
23
|
+
import type { TaskSummaryOutcome } from './task-summary-types.js';
|
|
24
|
+
/**
|
|
25
|
+
* Everything one call needs from its host, in the shape `AgentController`
|
|
26
|
+
* already uses for one-shot fast work (`createFastOverseerProvider`, F-16).
|
|
27
|
+
*
|
|
28
|
+
* THE KEY IS A GETTER, NOT A VALUE: `hasKey` / `getApiKey` read the live config
|
|
29
|
+
* and the environment, and a captured string would be the key from the moment
|
|
30
|
+
* the closure was built.
|
|
31
|
+
*/
|
|
32
|
+
export interface TaskSummaryCallDeps {
|
|
33
|
+
getConfig(): CliConfig;
|
|
34
|
+
hasKey(providerId: string, role?: ModelRole): boolean;
|
|
35
|
+
getApiKey(providerId: string, role?: ModelRole): string | undefined;
|
|
36
|
+
complete(providerId: string, request: LLMRequest): Promise<AssistantMessage>;
|
|
37
|
+
/** `config.taskSummary.fallback`, read at call time so `/config set` applies live. */
|
|
38
|
+
fallbackEnabled(): boolean;
|
|
39
|
+
/** The glyph this terminal renders for a cut label; supplied, never spelled here. */
|
|
40
|
+
ellipsis(): string;
|
|
41
|
+
}
|
|
42
|
+
export interface TaskSummaryCallParams {
|
|
43
|
+
/** The user's message (or the restored session's first user message). */
|
|
44
|
+
anchor: string;
|
|
45
|
+
/** Owned by the caller; aborted by the caller once this resolves. */
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
/** Epoch ms, injected so the timestamp is assertable. */
|
|
48
|
+
now: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Send one summary and bound it in time.
|
|
52
|
+
*
|
|
53
|
+
* WHY THE FALLBACK IS BUILT HERE AND NOT BY THE CALLER: every early return needs
|
|
54
|
+
* the same three arguments (anchor, reason, now) plus the user's `fallback`
|
|
55
|
+
* switch. Returning a bare reason and making the caller build the floor gives two
|
|
56
|
+
* places that can disagree about whether "no key" shows the user's own words.
|
|
57
|
+
*/
|
|
58
|
+
export declare function runTaskSummaryCall(deps: TaskSummaryCallDeps, params: TaskSummaryCallParams): Promise<TaskSummaryOutcome>;
|
|
59
|
+
/**
|
|
60
|
+
* The deps `AgentController` builds from its live fast wiring (F-16 shape).
|
|
61
|
+
*
|
|
62
|
+
* `fallbackEnabled` and `ellipsis` have defaults because the controller always
|
|
63
|
+
* knows the config but does not always have a terminal capability set: the
|
|
64
|
+
* header passes the real glyph, a headless caller gets the ASCII one.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createTaskSummaryDeps(input: {
|
|
67
|
+
getConfig(): CliConfig;
|
|
68
|
+
hasKey(providerId: string, role?: ModelRole): boolean;
|
|
69
|
+
getApiKey(providerId: string, role?: ModelRole): string | undefined;
|
|
70
|
+
complete(providerId: string, request: LLMRequest): Promise<AssistantMessage>;
|
|
71
|
+
ellipsis?: () => string;
|
|
72
|
+
}): TaskSummaryCallDeps;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bounded task-summary call (tui-shortcut-drawer-and-task-summary §6.2).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/fast/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* THIS MODULE OWNS ITS TIMER AND NOT ITS `AbortController`, which is the same
|
|
7
|
+
* prohibition `review-call.ts:7-29` states at length: a controller encapsulated
|
|
8
|
+
* with the call it aborts cannot be stamped by the lifecycle, so a cancellation
|
|
9
|
+
* and a timeout arrive as byte-identical bare errors. Here the consequence is
|
|
10
|
+
* cheaper (a label is decoration) but the rule is the same - the CALLER creates
|
|
11
|
+
* the controller, passes the signal, and aborts it once this function returns.
|
|
12
|
+
* `AgentController.summarizeTask` does exactly that, and it does so
|
|
13
|
+
* unconditionally, so a timed-out request never keeps a socket open.
|
|
14
|
+
*
|
|
15
|
+
* IT NEVER REJECTS. Every failure - four tier-off reasons, a missing key, a
|
|
16
|
+
* timeout, an abort, a provider error, an unusable reply - becomes a
|
|
17
|
+
* `TaskSummaryOutcome` carrying a `TaskSummaryReason`. A header label must not be
|
|
18
|
+
* able to produce an unhandled rejection in a TUI that has nowhere to show one.
|
|
19
|
+
*/
|
|
20
|
+
import { TASK_SUMMARY_LIMITS } from './limits.js';
|
|
21
|
+
import { resolveFastTier } from './resolve.js';
|
|
22
|
+
import { buildTaskSummaryRequest, fallbackView, interpretSummary, summaryBy, summaryText, } from './summary-prompt.js';
|
|
23
|
+
/** The tier-off reasons spelled the way the rest of the fast layer spells them. */
|
|
24
|
+
function tierReason(reason) {
|
|
25
|
+
if (reason === 'disabled')
|
|
26
|
+
return 'tier-off';
|
|
27
|
+
if (reason === 'no_model')
|
|
28
|
+
return 'no-model';
|
|
29
|
+
if (reason === 'no_adapter')
|
|
30
|
+
return 'no-adapter';
|
|
31
|
+
return 'no-key';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Send one summary and bound it in time.
|
|
35
|
+
*
|
|
36
|
+
* WHY THE FALLBACK IS BUILT HERE AND NOT BY THE CALLER: every early return needs
|
|
37
|
+
* the same three arguments (anchor, reason, now) plus the user's `fallback`
|
|
38
|
+
* switch. Returning a bare reason and making the caller build the floor gives two
|
|
39
|
+
* places that can disagree about whether "no key" shows the user's own words.
|
|
40
|
+
*/
|
|
41
|
+
export async function runTaskSummaryCall(deps, params) {
|
|
42
|
+
const fallback = (reason) => fallbackView({
|
|
43
|
+
anchor: params.anchor,
|
|
44
|
+
reason,
|
|
45
|
+
now: params.now,
|
|
46
|
+
fallbackEnabled: deps.fallbackEnabled(),
|
|
47
|
+
ellipsis: deps.ellipsis(),
|
|
48
|
+
});
|
|
49
|
+
// A prompt that is only whitespace has no label in it, and asking a model to
|
|
50
|
+
// summarise nothing burns a call to be told so.
|
|
51
|
+
if (params.anchor.trim().length === 0)
|
|
52
|
+
return fallback('empty');
|
|
53
|
+
if (params.signal?.aborted)
|
|
54
|
+
return fallback('aborted');
|
|
55
|
+
const config = deps.getConfig();
|
|
56
|
+
const tier = resolveFastTier(config, deps.hasKey);
|
|
57
|
+
// D-12: the MAIN model is never borrowed for this. A user without a fast tier
|
|
58
|
+
// has not bought the feature, and paying main-model latency for a header cell
|
|
59
|
+
// is the one trade this design refuses.
|
|
60
|
+
if (!tier.ok)
|
|
61
|
+
return fallback(tierReason(tier.reason));
|
|
62
|
+
const providerId = tier.ref.providerId;
|
|
63
|
+
const apiKey = deps.getApiKey(providerId, 'fast');
|
|
64
|
+
if (!apiKey)
|
|
65
|
+
return fallback('no-key');
|
|
66
|
+
const request = buildTaskSummaryRequest({
|
|
67
|
+
anchor: params.anchor,
|
|
68
|
+
providerId,
|
|
69
|
+
modelId: tier.ref.modelId,
|
|
70
|
+
apiKey,
|
|
71
|
+
...(tier.ref.baseUrl ? { baseUrl: tier.ref.baseUrl } : {}),
|
|
72
|
+
...(params.signal ? { signal: params.signal } : {}),
|
|
73
|
+
now: params.now,
|
|
74
|
+
});
|
|
75
|
+
let timer;
|
|
76
|
+
const timeout = new Promise((resolve) => {
|
|
77
|
+
timer = setTimeout(() => resolve('timeout'), TASK_SUMMARY_LIMITS.timeoutMs);
|
|
78
|
+
// NEVER KEEP THE PROCESS ALIVE FOR A HEADER LABEL: `unref` where the runtime
|
|
79
|
+
// offers it (Node does; the browser-shaped host this package is also built
|
|
80
|
+
// for does not) so a pending label cannot delay an exit.
|
|
81
|
+
if (typeof timer.unref === 'function') {
|
|
82
|
+
timer.unref();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
const raced = await Promise.race([
|
|
87
|
+
deps.complete(providerId, request),
|
|
88
|
+
timeout,
|
|
89
|
+
]);
|
|
90
|
+
if (raced === 'timeout')
|
|
91
|
+
return fallback('timeout');
|
|
92
|
+
const raw = summaryText(raced);
|
|
93
|
+
if (raw.length === 0)
|
|
94
|
+
return fallback('bad-output');
|
|
95
|
+
return interpretSummary({
|
|
96
|
+
raw,
|
|
97
|
+
anchor: params.anchor,
|
|
98
|
+
by: summaryBy(providerId, tier.ref.modelId),
|
|
99
|
+
now: params.now,
|
|
100
|
+
fallbackEnabled: deps.fallbackEnabled(),
|
|
101
|
+
ellipsis: deps.ellipsis(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// A cancellation is NOT a provider fault and must not be reported as one:
|
|
106
|
+
// the retry layer returns silently on abort, so both arrive as bare errors
|
|
107
|
+
// and the signal is the only witness that distinguishes them.
|
|
108
|
+
return fallback(params.signal?.aborted ? 'aborted' : 'error');
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (timer)
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The deps `AgentController` builds from its live fast wiring (F-16 shape).
|
|
117
|
+
*
|
|
118
|
+
* `fallbackEnabled` and `ellipsis` have defaults because the controller always
|
|
119
|
+
* knows the config but does not always have a terminal capability set: the
|
|
120
|
+
* header passes the real glyph, a headless caller gets the ASCII one.
|
|
121
|
+
*/
|
|
122
|
+
export function createTaskSummaryDeps(input) {
|
|
123
|
+
return {
|
|
124
|
+
getConfig: input.getConfig,
|
|
125
|
+
hasKey: input.hasKey,
|
|
126
|
+
getApiKey: input.getApiKey,
|
|
127
|
+
complete: input.complete,
|
|
128
|
+
// `!== false`, so a hand-built config without the section behaves like the
|
|
129
|
+
// shipped default (floor on) rather than like a dead feature.
|
|
130
|
+
fallbackEnabled: () => input.getConfig().taskSummary?.fallback !== false,
|
|
131
|
+
ellipsis: input.ellipsis ?? (() => '...'),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The task-summary prompt, its output normalisation, the language check and the
|
|
3
|
+
* deterministic floor (tui-shortcut-drawer-and-task-summary §4.4 / §6.2).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/fast/**` is inside the glyph scanner's scope. Every string in
|
|
6
|
+
* this file is English prose that goes to the model or into a code branch; the
|
|
7
|
+
* only non-ASCII the runtime ever sees arrives from the USER's message, which is
|
|
8
|
+
* data, not a literal.
|
|
9
|
+
*
|
|
10
|
+
* THE FLOOR NEVER LIES (D-9). Nine failure modes converge on one function,
|
|
11
|
+
* `localTaskSummary`, which returns a normalised prefix of the user's own words.
|
|
12
|
+
* That is the one outcome that cannot be wrong: the language matches by
|
|
13
|
+
* construction, nothing is invented, and the header cell is never empty. A model
|
|
14
|
+
* label that disagrees with the user's language is treated as a failure rather
|
|
15
|
+
* than displayed, because a Chinese task shown to a Chinese user as English is
|
|
16
|
+
* worse than their own sentence clipped.
|
|
17
|
+
*/
|
|
18
|
+
import type { AssistantMessage, LLMRequest } from '@aragon-agent/core';
|
|
19
|
+
import type { TaskSummaryOutcome, TaskSummarySpan, TaskSummaryView } from './task-summary-types.js';
|
|
20
|
+
/**
|
|
21
|
+
* The request one summary sends. No tools, no thinking, one bounded message.
|
|
22
|
+
*
|
|
23
|
+
* `modelLimits` is deliberately NOT set: the fast path uses the same output-cap
|
|
24
|
+
* authority as every other call (`resolveOutputTokens`), and hand-setting a
|
|
25
|
+
* second ceiling here is how the two would drift.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildTaskSummaryRequest(input: {
|
|
28
|
+
anchor: string;
|
|
29
|
+
providerId: string;
|
|
30
|
+
modelId: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
baseUrl?: string;
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
now: number;
|
|
35
|
+
}): LLMRequest;
|
|
36
|
+
/**
|
|
37
|
+
* `cjk` | `latin` | `other`, by LETTER majority.
|
|
38
|
+
*
|
|
39
|
+
* DIGITS AND PUNCTUATION ARE NOT LETTERS (the spec's wording) - a message that is
|
|
40
|
+
* only `pwd`'s output would otherwise be classified by its spaces. `other` is a
|
|
41
|
+
* real answer, not a failure: it means "the language rule cannot be checked",
|
|
42
|
+
* and the caller treats it as "no opinion" instead of a mismatch.
|
|
43
|
+
*/
|
|
44
|
+
export declare function dominantScript(text: string): TaskSummarySpan;
|
|
45
|
+
/**
|
|
46
|
+
* Clean one raw model reply into a displayable label.
|
|
47
|
+
*
|
|
48
|
+
* THE FIVE STEPS ARE SEPARATE AND IN THIS ORDER, so each is independently
|
|
49
|
+
* assertable (v2 P1-8):
|
|
50
|
+
*
|
|
51
|
+
* 1. strip ANSI escapes, then every remaining control character;
|
|
52
|
+
* 2. collapse every whitespace run (newlines included) to ONE space and trim;
|
|
53
|
+
* 3. strip a leading label prefix and any wrapping quotes / backticks / markers;
|
|
54
|
+
* 4. strip trailing punctuation, ASCII and CJK full-width;
|
|
55
|
+
* 5. cap by script - words to `summaryMaxWords` OR graphemes to
|
|
56
|
+
* `cjkMaxGraphemes` - then hard-cap characters at `summaryMaxChars`.
|
|
57
|
+
*
|
|
58
|
+
* Returns `null` when nothing survives; the caller reports that as `bad-output`
|
|
59
|
+
* and falls back. A refusal ("I cannot help with that") normalises to a non-empty
|
|
60
|
+
* string, so the LANGUAGE check is what rejects it - which is why step 5 runs
|
|
61
|
+
* before step 6 in `interpretSummary` rather than here.
|
|
62
|
+
*/
|
|
63
|
+
export declare function normalizeTaskSummary(raw: string): string | null;
|
|
64
|
+
/**
|
|
65
|
+
* The deterministic floor: a normalised verbatim prefix of the anchor.
|
|
66
|
+
*
|
|
67
|
+
* WHY IT IS SCRIPT-AWARE: a CJK sentence has no spaces, so a word-based clip
|
|
68
|
+
* would keep the first 8 characters and add an ellipsis - unreadable. CJK is
|
|
69
|
+
* clipped by graphemes instead, which is the same unit the prompt constrains.
|
|
70
|
+
*
|
|
71
|
+
* THE ELLIPSIS IS PASSED IN (`glyphs.ellipsis`) because this module may not
|
|
72
|
+
* spell a non-ASCII glyph (the scanner's rule); the caller supplies the one the
|
|
73
|
+
* terminal can actually render.
|
|
74
|
+
*/
|
|
75
|
+
export declare function localTaskSummary(anchor: string, span: TaskSummarySpan, ellipsis: string): string | null;
|
|
76
|
+
/** Build the fallback view, or `null` when the user switched the floor off. */
|
|
77
|
+
export declare function fallbackView(input: {
|
|
78
|
+
anchor: string;
|
|
79
|
+
reason: TaskSummaryView['reason'];
|
|
80
|
+
now: number;
|
|
81
|
+
fallbackEnabled: boolean;
|
|
82
|
+
ellipsis: string;
|
|
83
|
+
}): TaskSummaryOutcome;
|
|
84
|
+
/**
|
|
85
|
+
* Assemble the outcome from raw model text, applying D-9.
|
|
86
|
+
*
|
|
87
|
+
* ORDER MATTERS: normalise, then compare scripts, then decide. A model label in
|
|
88
|
+
* the wrong language is a FAILURE (`language-mismatch`), not something to
|
|
89
|
+
* display with a caveat - the fallback is the same length, in the right
|
|
90
|
+
* language, and true by construction.
|
|
91
|
+
*
|
|
92
|
+
* AND IT IS NOT RETRIED. A second call costs money and 1-3 seconds to reach an
|
|
93
|
+
* answer the fallback already gives correctly.
|
|
94
|
+
*/
|
|
95
|
+
export declare function interpretSummary(input: {
|
|
96
|
+
raw: string;
|
|
97
|
+
anchor: string;
|
|
98
|
+
by: string;
|
|
99
|
+
now: number;
|
|
100
|
+
fallbackEnabled: boolean;
|
|
101
|
+
ellipsis: string;
|
|
102
|
+
}): TaskSummaryOutcome;
|
|
103
|
+
/** The assistant text of a completion, or `''` for a tool-only answer. */
|
|
104
|
+
export declare function summaryText(message: AssistantMessage | undefined): string;
|
|
105
|
+
/** One model identity string, as the drawer's status line prints it. */
|
|
106
|
+
export declare function summaryBy(providerId: string, modelId: string): string;
|