@aragon-agent/cli 0.6.22 → 0.6.24

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.
Files changed (48) hide show
  1. package/README.md +20 -2
  2. package/dist/agent/controller.d.ts +11 -0
  3. package/dist/agent/controller.js +42 -4
  4. package/dist/agent/model-profile-settings.js +29 -2
  5. package/dist/agent/reducer.js +8 -6
  6. package/dist/commands/builtins.js +87 -3
  7. package/dist/compaction/child.d.ts +15 -0
  8. package/dist/compaction/child.js +14 -3
  9. package/dist/compaction/compactor.js +1 -0
  10. package/dist/compaction/context-command.js +10 -1
  11. package/dist/compaction/meter.d.ts +9 -0
  12. package/dist/compaction/meter.js +16 -1
  13. package/dist/compaction/pressure.d.ts +7 -0
  14. package/dist/compaction/pressure.js +5 -0
  15. package/dist/compaction/types.d.ts +11 -0
  16. package/dist/compaction/wiring.js +1 -0
  17. package/dist/config/cli-commands.d.ts +1 -1
  18. package/dist/config/cli-commands.js +12 -0
  19. package/dist/config/load.js +66 -9
  20. package/dist/config/model-profile-resolution.d.ts +8 -0
  21. package/dist/config/model-profile-resolution.js +13 -6
  22. package/dist/config/model-profile-store.d.ts +4 -2
  23. package/dist/config/model-profiles.d.ts +13 -0
  24. package/dist/config/model-profiles.js +18 -2
  25. package/dist/config/schema.d.ts +86 -0
  26. package/dist/config/schema.js +74 -0
  27. package/dist/team/model.d.ts +88 -0
  28. package/dist/team/model.js +136 -0
  29. package/dist/team/overseer.d.ts +5 -3
  30. package/dist/team/overseer.js +15 -4
  31. package/dist/team/report.d.ts +11 -0
  32. package/dist/team/report.js +24 -1
  33. package/dist/team/runtime.js +20 -3
  34. package/dist/team/subagent.d.ts +10 -0
  35. package/dist/team/subagent.js +57 -27
  36. package/dist/team/task-tool.d.ts +25 -6
  37. package/dist/team/task-tool.js +15 -9
  38. package/dist/team/types.d.ts +4 -3
  39. package/dist/ui/App.js +5 -0
  40. package/dist/ui/overlays/ModelProfileEditor.js +18 -4
  41. package/dist/ui/overlays/ModelProfilePicker.d.ts +14 -2
  42. package/dist/ui/overlays/ModelProfilePicker.js +51 -10
  43. package/dist/ui/overlays/QuestionOverlay.js +47 -6
  44. package/dist/ui/overlays/SettingsScreen.d.ts +9 -0
  45. package/dist/ui/overlays/SettingsScreen.js +119 -17
  46. package/dist/ui/overlays/model-profile-state.d.ts +53 -4
  47. package/dist/ui/overlays/model-profile-state.js +111 -14
  48. package/package.json +2 -2
@@ -1,17 +1,29 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { emptyModelProfiles, duplicateModelProfile, normalizeModelProfiles, validateModelProfiles } from '../../config/model-profiles.js';
3
+ import { parseContextWindowInput } from '../../config/schema.js';
4
+ /** The picker's built-in `Fast tier model` row; never a profile id on disk. */
5
+ export const SUBAGENT_FAST_ROW_ID = '__fast__';
3
6
  /** Keep the original secret separate from the empty replacement field. */
4
7
  export function createProfileEditor(profile) {
5
8
  return { original: { ...profile }, name: profile.name, provider: profile.provider,
6
9
  model: profile.model, baseUrl: profile.baseUrl ?? '', apiKey: '',
10
+ contextWindow: profile.contextWindow === null ? '' : String(profile.contextWindow),
7
11
  credentialMode: profile.apiKey ? 'keep' : 'shared', endpointChanged: false };
8
12
  }
9
13
  /** Validate locally before merging the child form into the root transaction. */
10
14
  export function finishProfileEditor(editor) {
15
+ // STRICT IN THE UI, TOLERANT ON DISK (§6.5 / invariant 4): a typo is refused
16
+ // here with an inline error and the focus jump its `field` enables; it never
17
+ // reaches the draft. The message prefix matches the editor's LABELS row.
18
+ const window = parseContextWindowInput(editor.contextWindow);
19
+ if (window.kind === 'invalid') {
20
+ return { error: 'Context window: use a number or auto.', field: 'contextWindow' };
21
+ }
11
22
  const profile = { id: editor.original.id, name: editor.name.trim(),
12
23
  provider: editor.provider, model: editor.model.trim(), baseUrl: editor.baseUrl.trim() || null,
13
24
  apiKey: editor.credentialMode === 'keep' ? editor.original.apiKey
14
- : editor.credentialMode === 'replace' ? editor.apiKey.trim() : null };
25
+ : editor.credentialMode === 'replace' ? editor.apiKey.trim() : null,
26
+ contextWindow: window.kind === 'auto' ? null : window.value };
15
27
  const library = { ...emptyModelProfiles(), entries: [profile] };
16
28
  const issue = validateModelProfiles(library)[0];
17
29
  if (issue)
@@ -30,10 +42,52 @@ export function createModelProfileState(draft) {
30
42
  bindNew: false, dirty: false };
31
43
  }
32
44
  /** Describe references using text so ASCII and no-color modes remain usable. */
33
- export function profileUsers(profiles, id) {
34
- return [profiles?.mainId === id ? 'Main' : '', profiles?.fastId === id ? 'Fast' : '']
45
+ export function profileUsers(profiles, id, subagentProfileId) {
46
+ return [profiles?.mainId === id ? 'Main' : '', profiles?.fastId === id ? 'Fast' : '',
47
+ subagentProfileId === id ? 'Subagent' : '']
35
48
  .filter(Boolean).join(' + ');
36
49
  }
50
+ /**
51
+ * The team section a draft would save: the patch first, then the disk baseline
52
+ * (subagent-model §3.6). The explicit-`undefined` test is what makes an
53
+ * explicit `null` in the patch win over the baseline - `??` would fold it away.
54
+ */
55
+ export function draftTeam(draft) {
56
+ const patch = draft.patch.team;
57
+ const baseline = draft.baseline.team;
58
+ return {
59
+ model: patch?.model !== undefined ? patch.model : baseline?.model ?? 'main',
60
+ modelProfileId: patch?.modelProfileId !== undefined ? patch.modelProfileId
61
+ : baseline?.modelProfileId ?? null,
62
+ };
63
+ }
64
+ /** The profile id the draft's team section binds, for reference checks (3.7). */
65
+ export function draftSubagentProfileId(draft) {
66
+ return draftTeam(draft).model === 'profile' ? draftTeam(draft).modelProfileId : null;
67
+ }
68
+ /** The projected picker selection for the subagent role (6.2). */
69
+ export function subagentSelectedId(draft) {
70
+ const team = draftTeam(draft);
71
+ if (team.model === 'profile')
72
+ return team.modelProfileId;
73
+ return team.model === 'fast' ? SUBAGENT_FAST_ROW_ID : null;
74
+ }
75
+ export function subagentModelSummary(input) {
76
+ if (input.mode === 'main')
77
+ return `Same as main (${input.mainConnection})`;
78
+ if (input.mode === 'fast') {
79
+ return input.fastConnection
80
+ ? `Fast tier (${input.fastConnection})`
81
+ : 'Fast tier (off - falls back to main)';
82
+ }
83
+ const entry = input.modelProfileId
84
+ ? input.profiles?.entries.find((candidate) => candidate.id === input.modelProfileId)
85
+ : undefined;
86
+ if (!entry)
87
+ return 'Profile missing - falls back to main';
88
+ const missing = !entry.apiKey && !input.hasKey(entry.provider);
89
+ return `${entry.name} ${entry.provider}:${entry.model}${missing ? ' [Missing API key]' : ''}`;
90
+ }
37
91
  /** Adopt persisted fields only, resolving the old fast inheritance once. */
38
92
  export function createCustomProfile(draft, role) {
39
93
  const file = draft.baseline;
@@ -45,7 +99,11 @@ export function createCustomProfile(draft, role) {
45
99
  || (provider === (file.provider || 'anthropic') ? file.baseUrl : null)
46
100
  : file.baseUrl) || null;
47
101
  return { id: randomUUID(), name: '', provider, model, baseUrl,
48
- apiKey: role === 'fast' && !model ? null : file.apiKeys?.[provider] || null };
102
+ apiKey: role === 'fast' && !model ? null : file.apiKeys?.[provider] || null,
103
+ // Carry the user's existing top-level correction into the first profile
104
+ // (§6.5): binding an AUTO profile would otherwise silently retire it
105
+ // (tradeoff B's migration risk, mitigated exactly here).
106
+ contextWindow: file.contextWindow ?? null };
49
107
  }
50
108
  function editField(state, action) {
51
109
  if (!state.editor)
@@ -85,9 +143,24 @@ export function modelProfileReducer(state, action) {
85
143
  case 'reload': return createModelProfileState(action.draft);
86
144
  case 'page': return { ...state, page: action.page, role: action.role ?? state.role,
87
145
  error: undefined };
88
- case 'select': return { ...state, page: 'root', dirty: true, error: undefined,
89
- draft: { ...state.draft, profiles: { ...library, [`${action.role}Id`]: action.id },
90
- activateRoles: [...new Set([...state.draft.activateRoles, action.role])] } };
146
+ case 'select': {
147
+ // THE SUBAGENT BRANCH (D-5): selection writes the DRAFT's team patch -
148
+ // `profiles` and `activateRoles` are untouched, because 'subagent' is a
149
+ // picker-only role with no `${role}Id` library key (6.2). Save is the
150
+ // one place the patch ever reaches disk (I-5).
151
+ if (action.role === 'subagent') {
152
+ const team = action.id === null
153
+ ? { model: 'main', modelProfileId: null }
154
+ : action.id === SUBAGENT_FAST_ROW_ID
155
+ ? { model: 'fast', modelProfileId: null }
156
+ : { model: 'profile', modelProfileId: action.id };
157
+ return { ...state, page: 'root', dirty: true, error: undefined,
158
+ draft: { ...state.draft, patch: { ...state.draft.patch, team } } };
159
+ }
160
+ return { ...state, page: 'root', dirty: true, error: undefined,
161
+ draft: { ...state.draft, profiles: { ...library, [`${action.role}Id`]: action.id },
162
+ activateRoles: [...new Set([...state.draft.activateRoles, action.role])] } };
163
+ }
91
164
  case 'edit': return { ...state, editor: createProfileEditor(action.profile), page: 'editor',
92
165
  returnPage: state.page === 'picker' ? 'picker' : 'manager',
93
166
  bindNew: action.bindNew ?? false, error: undefined };
@@ -107,13 +180,37 @@ export function modelProfileReducer(state, action) {
107
180
  : state;
108
181
  }
109
182
  case 'delete': {
110
- const users = profileUsers(library, action.id);
111
- return users ? { ...state, error: `Used by ${users}. Select another profile first.` }
112
- : { ...state, page: 'delete-confirm', deleteId: action.id, error: undefined };
183
+ // UNBIND-AND-DELETE (D-6): being referenced no longer refuses the flow.
184
+ // The references - subagent included - are computed here, shown on the
185
+ // confirm page, and cleared by `confirm-delete` inside the draft.
186
+ const users = profileUsers(library, action.id, draftSubagentProfileId(state.draft));
187
+ return { ...state, page: 'delete-confirm', deleteId: action.id,
188
+ deleteUsers: users, error: undefined };
189
+ }
190
+ case 'confirm-delete': {
191
+ const id = state.deleteId;
192
+ if (!id)
193
+ return state;
194
+ // THE DRAFT-SIDE UNBIND: Main/Fast bindings pointing at the entry are
195
+ // nulled here (not refused) and the subagent binding resets to main
196
+ // when it named THIS entry. `removeModelProfile`'s throw stays as the
197
+ // bottom-line defense - the UI has already unbound, so it cannot fire
198
+ // on this path.
199
+ const profiles = {
200
+ ...library,
201
+ mainId: library.mainId === id ? null : library.mainId,
202
+ fastId: library.fastId === id ? null : library.fastId,
203
+ entries: library.entries.filter((entry) => entry.id !== id),
204
+ };
205
+ const teamReset = draftSubagentProfileId(state.draft) === id
206
+ ? { team: { model: 'main', modelProfileId: null } }
207
+ : {};
208
+ return { ...state, page: 'manager', dirty: true,
209
+ draft: { ...state.draft, profiles,
210
+ patch: { ...state.draft.patch, ...teamReset } },
211
+ deleteId: undefined, deleteUsers: undefined,
212
+ // No orphan editor for a deleted entry (3.7).
213
+ editor: state.editor?.original.id === id ? undefined : state.editor };
113
214
  }
114
- case 'confirm-delete': return { ...state, page: 'manager', dirty: true,
115
- draft: { ...state.draft, profiles: { ...library,
116
- entries: library.entries.filter((entry) => entry.id !== state.deleteId) } },
117
- deleteId: undefined };
118
215
  }
119
216
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aragon-agent/cli",
3
- "version": "0.6.22",
3
+ "version": "0.6.24",
4
4
  "description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
5
5
  "license": "MIT",
6
6
  "author": "AragonAgent contributors",
@@ -56,7 +56,7 @@
56
56
  "prepublishOnly": "npm run build"
57
57
  },
58
58
  "dependencies": {
59
- "@aragon-agent/core": "^0.2.34",
59
+ "@aragon-agent/core": "^0.2.36",
60
60
  "chalk": "^5.6.2",
61
61
  "cli-highlight": "^2.0.0",
62
62
  "commander": "^12.1.0",