@aragon-agent/cli 0.6.37 → 0.6.39
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 +2523 -2326
- package/README.md +3614 -3173
- package/dist/agent/controller.d.ts +142 -1
- package/dist/agent/controller.js +171 -3
- package/dist/agent/reducer.d.ts +41 -1
- package/dist/agent/reducer.js +25 -0
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +5 -0
- package/dist/cli.js +85 -27
- package/dist/commands/builtins.js +13 -1
- package/dist/commands/keys-command.d.ts +23 -0
- package/dist/commands/keys-command.js +38 -0
- package/dist/commands/perf.d.ts +7 -1
- package/dist/commands/perf.js +2 -2
- package/dist/commands/registry.d.ts +39 -0
- package/dist/commands/task-command.d.ts +39 -0
- package/dist/commands/task-command.js +102 -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 +6 -1
- package/dist/config/schema.d.ts +43 -4
- package/dist/config/schema.js +31 -5
- package/dist/config/store.js +17 -2
- 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/output-streams.d.ts +50 -0
- package/dist/proc/output-streams.js +54 -0
- 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/tools/bash-tool.d.ts +10 -0
- package/dist/tools/bash-tool.js +71 -6
- package/dist/tools/index.d.ts +93 -1
- package/dist/tools/index.js +112 -3
- package/dist/tools/tool-gate.d.ts +100 -0
- package/dist/tools/tool-gate.js +87 -0
- package/dist/ui/App.js +224 -11
- package/dist/ui/Header.d.ts +28 -16
- package/dist/ui/Header.js +21 -19
- 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/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/ShortcutPanel.d.ts +39 -0
- package/dist/ui/overlays/ShortcutPanel.js +106 -0
- package/dist/ui/scrollback/canvas.d.ts +3 -2
- package/dist/ui/scrollback/canvas.js +3 -2
- package/dist/ui/scrollback/scrollback-writer.d.ts +13 -1
- package/dist/ui/scrollback/scrollback-writer.js +109 -10
- package/dist/ui/shortcuts.d.ts +81 -0
- package/dist/ui/shortcuts.js +221 -0
- package/package.json +2 -2
package/dist/config/schema.d.ts
CHANGED
|
@@ -886,8 +886,6 @@ export interface PromptsConfig {
|
|
|
886
886
|
* `insert` keeps the user's words; `replace` overwrites them.
|
|
887
887
|
*/
|
|
888
888
|
insertMode: 'insert' | 'replace';
|
|
889
|
-
/** Whether the header's right-hand column may teach `^P prompts`. */
|
|
890
|
-
hint: boolean;
|
|
891
889
|
}
|
|
892
890
|
export declare const DEFAULT_PROMPTS_CONFIG: PromptsConfig;
|
|
893
891
|
/**
|
|
@@ -899,6 +897,35 @@ export declare const DEFAULT_PROMPTS_CONFIG: PromptsConfig;
|
|
|
899
897
|
* what the user has already typed into the composer.
|
|
900
898
|
*/
|
|
901
899
|
export declare function clampPromptsConfig(raw: unknown): PromptsConfig;
|
|
900
|
+
/**
|
|
901
|
+
* The task label's two switches (tui-shortcut-drawer-and-task-summary §4.8).
|
|
902
|
+
*
|
|
903
|
+
* ONE LEVEL DEEP, like every section beside it: `runConfigGet` splits on a single
|
|
904
|
+
* dot, so `taskSummary.enabled` resolves and a nested key would not (F-18).
|
|
905
|
+
*
|
|
906
|
+
* NEITHER KEY IS ABOUT THE LABEL'S CONTENT. `enabled` is the kill switch: off
|
|
907
|
+
* means no call is made and no cell is drawn. `fallback` decides what happens when
|
|
908
|
+
* the call cannot produce a label - the deterministic verbatim-prefix floor, or
|
|
909
|
+
* nothing at all. Both are BEHAVIOUR, which is why they are here rather than in
|
|
910
|
+
* `fast/limits.ts` (F-13).
|
|
911
|
+
*/
|
|
912
|
+
export interface TaskSummaryConfig {
|
|
913
|
+
/** The kill switch. `false` removes the header cell and the automatic call. */
|
|
914
|
+
enabled: boolean;
|
|
915
|
+
/** The deterministic verbatim-prefix floor (D-9). `false` = show nothing instead. */
|
|
916
|
+
fallback: boolean;
|
|
917
|
+
}
|
|
918
|
+
export declare const DEFAULT_TASK_SUMMARY_CONFIG: TaskSummaryConfig;
|
|
919
|
+
/**
|
|
920
|
+
* THE single gate for every `taskSummary` read AND write, like the twelve sections
|
|
921
|
+
* above it.
|
|
922
|
+
*
|
|
923
|
+
* BOTH DEFAULTS ARE `true`, AND THE DIRECTION MATTERS: a corrupt or hand-edited
|
|
924
|
+
* section must not silently switch off a feature the user asked for, and - more
|
|
925
|
+
* importantly - it must not switch off the FLOOR, which is the only thing standing
|
|
926
|
+
* between a failed call and an empty header cell.
|
|
927
|
+
*/
|
|
928
|
+
export declare function clampTaskSummaryConfig(raw: unknown): TaskSummaryConfig;
|
|
902
929
|
/**
|
|
903
930
|
* Parse `0.9`, `90%` or `90` into a ratio, or `null` when it is neither.
|
|
904
931
|
*
|
|
@@ -1061,8 +1088,10 @@ export interface PersistedConfig {
|
|
|
1061
1088
|
bash: BashConfig;
|
|
1062
1089
|
/** Background jobs and bounded waiting. The ELEVENTH; scalars only (see JobsConfig). */
|
|
1063
1090
|
jobs: JobsConfig;
|
|
1064
|
-
/** Quick prompts. The TWELFTH;
|
|
1091
|
+
/** Quick prompts. The TWELFTH; two scalars (see PromptsConfig). */
|
|
1065
1092
|
prompts: PromptsConfig;
|
|
1093
|
+
/** The header task label. The THIRTEENTH; two scalars (see TaskSummaryConfig). */
|
|
1094
|
+
taskSummary: TaskSummaryConfig;
|
|
1066
1095
|
}
|
|
1067
1096
|
export declare const DEFAULT_CONFIG: PersistedConfig;
|
|
1068
1097
|
/**
|
|
@@ -1406,7 +1435,7 @@ export interface CliConfig {
|
|
|
1406
1435
|
* scalars, one level deep.
|
|
1407
1436
|
*
|
|
1408
1437
|
* ON `CliConfig` AS WELL AS `PersistedConfig`, for the reason every section above
|
|
1409
|
-
* records: `App` reads `cfg.prompts?.enabled` / `.
|
|
1438
|
+
* records: `App` reads `cfg.prompts?.enabled` / `.insertMode` from a
|
|
1410
1439
|
* `CliConfig`, so a key added to the persisted shape alone would round-trip
|
|
1411
1440
|
* through the `config` commands, appear in `config list`, and change nothing.
|
|
1412
1441
|
*
|
|
@@ -1419,6 +1448,16 @@ export interface CliConfig {
|
|
|
1419
1448
|
* disabled or destructive one.
|
|
1420
1449
|
*/
|
|
1421
1450
|
prompts?: PromptsConfig;
|
|
1451
|
+
/**
|
|
1452
|
+
* The header task label (tui-shortcut-drawer-and-task-summary §4.8). The
|
|
1453
|
+
* THIRTEENTH nested object; two scalars, one level deep.
|
|
1454
|
+
*
|
|
1455
|
+
* OPTIONAL HERE FOR THE SAME REASON AS `prompts` ABOVE: `CliConfig` is built by
|
|
1456
|
+
* hand in many fixtures, and every reader uses `!== false`, so a missing section
|
|
1457
|
+
* behaves exactly like a default session (label on, floor on) rather than like a
|
|
1458
|
+
* switched-off feature.
|
|
1459
|
+
*/
|
|
1460
|
+
taskSummary?: TaskSummaryConfig;
|
|
1422
1461
|
/**
|
|
1423
1462
|
* Lifetime submit count driving the composer hint fade (`HINT_FADE_AFTER`).
|
|
1424
1463
|
*
|
package/dist/config/schema.js
CHANGED
|
@@ -966,7 +966,6 @@ export function clampJobsConfig(raw) {
|
|
|
966
966
|
export const DEFAULT_PROMPTS_CONFIG = {
|
|
967
967
|
enabled: true,
|
|
968
968
|
insertMode: 'insert',
|
|
969
|
-
hint: true,
|
|
970
969
|
};
|
|
971
970
|
const PROMPT_INSERT_MODES = ['insert', 'replace'];
|
|
972
971
|
/**
|
|
@@ -985,7 +984,26 @@ export function clampPromptsConfig(raw) {
|
|
|
985
984
|
insertMode: PROMPT_INSERT_MODES.includes(mode)
|
|
986
985
|
? mode
|
|
987
986
|
: DEFAULT_PROMPTS_CONFIG.insertMode,
|
|
988
|
-
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
export const DEFAULT_TASK_SUMMARY_CONFIG = {
|
|
990
|
+
enabled: true,
|
|
991
|
+
fallback: true,
|
|
992
|
+
};
|
|
993
|
+
/**
|
|
994
|
+
* THE single gate for every `taskSummary` read AND write, like the twelve sections
|
|
995
|
+
* above it.
|
|
996
|
+
*
|
|
997
|
+
* BOTH DEFAULTS ARE `true`, AND THE DIRECTION MATTERS: a corrupt or hand-edited
|
|
998
|
+
* section must not silently switch off a feature the user asked for, and - more
|
|
999
|
+
* importantly - it must not switch off the FLOOR, which is the only thing standing
|
|
1000
|
+
* between a failed call and an empty header cell.
|
|
1001
|
+
*/
|
|
1002
|
+
export function clampTaskSummaryConfig(raw) {
|
|
1003
|
+
const src = (raw && typeof raw === 'object' ? raw : {});
|
|
1004
|
+
return {
|
|
1005
|
+
enabled: bool(src.enabled, DEFAULT_TASK_SUMMARY_CONFIG.enabled),
|
|
1006
|
+
fallback: bool(src.fallback, DEFAULT_TASK_SUMMARY_CONFIG.fallback),
|
|
989
1007
|
};
|
|
990
1008
|
}
|
|
991
1009
|
/**
|
|
@@ -1093,14 +1111,22 @@ export const DEFAULT_CONFIG = {
|
|
|
1093
1111
|
// rather than quietly disabling something.
|
|
1094
1112
|
jobs: DEFAULT_JOBS_CONFIG,
|
|
1095
1113
|
// And one section later again, for the seventh time, and this one has BOTH
|
|
1096
|
-
// failure directions at once: `prompts.enabled`
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1114
|
+
// failure directions at once: `prompts.enabled` is `true` by default, so a
|
|
1115
|
+
// `config.json` predating this feature would resolve it to `undefined` (falsy)
|
|
1116
|
+
// and arrive with the key silently off - while
|
|
1099
1117
|
// `prompts.insertMode` would be `undefined`, which is neither `'insert'` nor
|
|
1100
1118
|
// `'replace'` and would make `planPromptFill` fall through to its `replace`
|
|
1101
1119
|
// branch. That branch discards the user's own words, so the miss would be a data
|
|
1102
1120
|
// loss rather than a missing hint.
|
|
1103
1121
|
prompts: DEFAULT_PROMPTS_CONFIG,
|
|
1122
|
+
// And one section later again, for the eighth time - and this one has a silent
|
|
1123
|
+
// DATA-LOSS-shaped miss: `taskSummary.enabled` and `.fallback` are both `true` by
|
|
1124
|
+
// default, and `App` reads `cfg.taskSummary?.fallback !== false` at call time. A
|
|
1125
|
+
// `config.json` predating this feature would resolve them to `undefined`, which
|
|
1126
|
+
// happens to behave like the defaults in those two readers but NOT in
|
|
1127
|
+
// `config list`, so the file would report a section it never wrote. One line
|
|
1128
|
+
// here, like the seven above it.
|
|
1129
|
+
taskSummary: DEFAULT_TASK_SUMMARY_CONFIG,
|
|
1104
1130
|
};
|
|
1105
1131
|
/**
|
|
1106
1132
|
* Runtime state that used to be stored in `config.json` by mistake
|
package/dist/config/store.js
CHANGED
|
@@ -17,7 +17,7 @@ import { getConfigPath, getSessionsDir } from './app-paths.js';
|
|
|
17
17
|
import { registerProfileSecrets, registerSecretsFrom } from '../logging/secret-registry.js';
|
|
18
18
|
import { adaptLegacyConnectionPatch, ModelProfileConfigError, readModelSettingsDisk } from './model-profile-store.js';
|
|
19
19
|
import { normalizeModelProfiles } from './model-profiles.js';
|
|
20
|
-
import { clampBashConfig, clampJobsConfig, clampPromptsConfig, CONFIG_VERSION, DEFAULT_CONFIG, clampCompactionConfig, clampFastConfig, clampLogConfig, clampMaxTokens, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampContextWindow, clampUpdateConfig, isAutoToken, stripLegacyStateKeys, } from './schema.js';
|
|
20
|
+
import { clampBashConfig, clampJobsConfig, clampPromptsConfig, clampTaskSummaryConfig, CONFIG_VERSION, DEFAULT_CONFIG, clampCompactionConfig, clampFastConfig, clampLogConfig, clampMaxTokens, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampContextWindow, clampUpdateConfig, isAutoToken, stripLegacyStateKeys, } from './schema.js';
|
|
21
21
|
/**
|
|
22
22
|
* Normalize `maxTokens` for both directions of the config file.
|
|
23
23
|
*
|
|
@@ -200,6 +200,14 @@ export function normalizePersistedConfig(input) {
|
|
|
200
200
|
// `'insert'` nor `'replace'`, so a fill into a non-empty draft would take the
|
|
201
201
|
// branch that REPLACES what the user typed.
|
|
202
202
|
prompts: clampPromptsConfig({ ...DEFAULT_CONFIG.prompts, ...(partial.prompts ?? {}) }),
|
|
203
|
+
// And the thirteenth, on the same line and for the same reason. Without it a
|
|
204
|
+
// `config.json` predating the header label resolves the whole section to
|
|
205
|
+
// `undefined`, and the two readers disagree: `App`'s `!== false` checks would
|
|
206
|
+
// treat it as ON while `config list` would report nothing at all.
|
|
207
|
+
taskSummary: clampTaskSummaryConfig({
|
|
208
|
+
...DEFAULT_CONFIG.taskSummary,
|
|
209
|
+
...(partial.taskSummary ?? {}),
|
|
210
|
+
}),
|
|
203
211
|
};
|
|
204
212
|
}
|
|
205
213
|
/**
|
|
@@ -308,11 +316,18 @@ export function mergePersistedConfig(current, patch) {
|
|
|
308
316
|
// user would never learn that their privacy switch had been turned back on.
|
|
309
317
|
jobs: clampJobsConfig({ ...current.jobs, ...(patch.jobs ?? {}) }),
|
|
310
318
|
// The twelfth section, and the seventh time this file records the same trap.
|
|
311
|
-
// `aragon config set prompts.
|
|
319
|
+
// `aragon config set prompts.insertMode replace` sends `{ prompts: { insertMode } }`;
|
|
312
320
|
// without this line the shallow top-level spread REPLACES the section and
|
|
313
321
|
// `insertMode` is written absent - which reads back as neither `'insert'` nor
|
|
314
322
|
// `'replace'`, i.e. the next fill overwrites the user's own draft.
|
|
315
323
|
prompts: clampPromptsConfig({ ...current.prompts, ...(patch.prompts ?? {}) }),
|
|
324
|
+
// The thirteenth section, and the eighth time this file records the same trap.
|
|
325
|
+
// `aragon config set taskSummary.fallback false` sends
|
|
326
|
+
// `{ taskSummary: { fallback: false } }`; without this line the shallow spread
|
|
327
|
+
// REPLACES the section and `enabled` is written absent - and the one direction
|
|
328
|
+
// that must never happen is a FAILED summary call landing on "show nothing"
|
|
329
|
+
// because a switch the user never touched defaulted away.
|
|
330
|
+
taskSummary: clampTaskSummaryConfig({ ...current.taskSummary, ...(patch.taskSummary ?? {}) }),
|
|
316
331
|
};
|
|
317
332
|
return merged;
|
|
318
333
|
}
|
|
@@ -27,3 +27,12 @@ export interface DoctorOptions {
|
|
|
27
27
|
probe?: boolean;
|
|
28
28
|
}
|
|
29
29
|
export declare function runDoctorChecks(flags: CliFlags, opts?: DoctorOptions): Promise<DoctorReport>;
|
|
30
|
+
/**
|
|
31
|
+
* Is the optional JSON-Schema validator importable?
|
|
32
|
+
*
|
|
33
|
+
* ASKED ONCE, HERE, AND REUSED BY `aragon info` - because "how strict is my result
|
|
34
|
+
* schema validated" is a property of this MACHINE, not of the contract, and there must
|
|
35
|
+
* be exactly one answer to it. `createRequire().resolve()` rather than `import('ajv')`
|
|
36
|
+
* because `info` is synchronous: resolution is the question, not loading.
|
|
37
|
+
*/
|
|
38
|
+
export declare function hasAjvValidator(): boolean;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* the caller guess, which is the state this command exists to end.
|
|
11
11
|
*/
|
|
12
12
|
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
13
14
|
import { join } from 'node:path';
|
|
14
15
|
import process from 'node:process';
|
|
15
16
|
import { initProviders, ModelRegistry } from '@aragon-agent/core';
|
|
@@ -26,10 +27,43 @@ export async function runDoctorChecks(flags, opts = {}) {
|
|
|
26
27
|
checks.push(checkProviderKey(config));
|
|
27
28
|
checks.push(checkModel(config));
|
|
28
29
|
checks.push(checkSessionsDir());
|
|
30
|
+
checks.push(checkValidationStrength());
|
|
29
31
|
if (opts.probe)
|
|
30
32
|
checks.push(await probeProvider(config));
|
|
31
33
|
return { ok: checks.every((c) => c.verdict !== 'fail'), checks };
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Is the optional JSON-Schema validator importable?
|
|
37
|
+
*
|
|
38
|
+
* ASKED ONCE, HERE, AND REUSED BY `aragon info` - because "how strict is my result
|
|
39
|
+
* schema validated" is a property of this MACHINE, not of the contract, and there must
|
|
40
|
+
* be exactly one answer to it. `createRequire().resolve()` rather than `import('ajv')`
|
|
41
|
+
* because `info` is synchronous: resolution is the question, not loading.
|
|
42
|
+
*/
|
|
43
|
+
export function hasAjvValidator() {
|
|
44
|
+
try {
|
|
45
|
+
createRequire(import.meta.url).resolve('ajv');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `ajv: present | absent` - a `pass`, never a `warn`.
|
|
54
|
+
*
|
|
55
|
+
* BOTH ANSWERS ARE CORRECT INSTALLS. `ajv` is an OPTIONAL dependency of Core, and its
|
|
56
|
+
* absence degrades validation honestly (the CLI's own belt still enforces `required`,
|
|
57
|
+
* recursive `properties`, `items` and `additionalProperties:false`). The check exists
|
|
58
|
+
* because the STRENGTH differs and nothing else in the product says so out loud.
|
|
59
|
+
*/
|
|
60
|
+
function checkValidationStrength() {
|
|
61
|
+
if (hasAjvValidator()) {
|
|
62
|
+
return pass('ajv', 'present: full JSON Schema keywords are enforced');
|
|
63
|
+
}
|
|
64
|
+
return pass('ajv', 'absent: required/properties/items/additionalProperties are still enforced by the ' +
|
|
65
|
+
'built-in validator; pattern/format/multipleOf are not');
|
|
66
|
+
}
|
|
33
67
|
function pass(name, detail) {
|
|
34
68
|
return { name, verdict: 'pass', detail, remedy: '' };
|
|
35
69
|
}
|
|
@@ -26,5 +26,76 @@ export interface ExecInfo {
|
|
|
26
26
|
inputFormats: string[];
|
|
27
27
|
permissionModes: string[];
|
|
28
28
|
features: string[];
|
|
29
|
+
/**
|
|
30
|
+
* The bridge protocol, as data (headless-host-bridge section 4.6).
|
|
31
|
+
*
|
|
32
|
+
* THE POINT OF THIS BLOCK IS THAT A CALLER SHOULD NOT HAVE TO PROBE. The
|
|
33
|
+
* protocol version, which decision kinds exist, which tool names are gated and
|
|
34
|
+
* the limits it will be held to are all answered here, because the alternative
|
|
35
|
+
* is "send a frame and see whether an error comes back" - discovery by damage.
|
|
36
|
+
*/
|
|
37
|
+
bridge: {
|
|
38
|
+
protocolVersion: number;
|
|
39
|
+
capabilities: string[];
|
|
40
|
+
decisionKinds: string[];
|
|
41
|
+
gatedTools: string[];
|
|
42
|
+
limits: {
|
|
43
|
+
maxCallerTools: number;
|
|
44
|
+
maxFrameBytes: number;
|
|
45
|
+
defaultDecisionTimeoutMs: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The per-request ceilings (headless-request-scope-and-typed-result section 4.6),
|
|
49
|
+
* BESIDE `limits` rather than inside it: `limits` is what one FRAME may carry,
|
|
50
|
+
* these bound one REQUEST. Both read their own `*_LIMITS` constant, so neither
|
|
51
|
+
* restates a number the enforcement does not use.
|
|
52
|
+
*/
|
|
53
|
+
requestLimits: {
|
|
54
|
+
maxRequestTurns: number;
|
|
55
|
+
maxRequestTimeoutMs: number;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The thread ceilings (headless-threads-and-lifecycle-hooks section 4.6).
|
|
59
|
+
*
|
|
60
|
+
* A SIBLING OF `limits` / `requestLimits` RATHER THAN A MEMBER, because the three answer
|
|
61
|
+
* three different questions: what one frame carries, what one request may spend, and how
|
|
62
|
+
* many conversations this process can host.
|
|
63
|
+
*/
|
|
64
|
+
threadLimits: {
|
|
65
|
+
maxOpenThreads: number;
|
|
66
|
+
defaultMaxOpenThreads: number;
|
|
67
|
+
defaultConcurrency: number;
|
|
68
|
+
maxThreadIdLength: number;
|
|
69
|
+
};
|
|
70
|
+
/** The hook ceilings and the four points, so a wrapper need not probe for them. */
|
|
71
|
+
hookLimits: {
|
|
72
|
+
maxHooks: number;
|
|
73
|
+
maxHooksPerPoint: number;
|
|
74
|
+
defaultTimeoutMs: number;
|
|
75
|
+
minTimeoutMs: number;
|
|
76
|
+
maxTimeoutMs: number;
|
|
77
|
+
allowModeMaxTimeoutMs: number;
|
|
78
|
+
points: string[];
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* The structured-result contract, as data (section 4.6).
|
|
83
|
+
*
|
|
84
|
+
* A NEW BLOCK, so an older consumer simply ignores it. It exists because the
|
|
85
|
+
* STRENGTH of the validation is machine-dependent (`ajv` is an optional dependency
|
|
86
|
+
* of Core), and "which strength am I on" is not something a caller can discover by
|
|
87
|
+
* sending a frame.
|
|
88
|
+
*/
|
|
89
|
+
result: {
|
|
90
|
+
schemaBytes: number;
|
|
91
|
+
schemaDepth: number;
|
|
92
|
+
schemaRefs: number;
|
|
93
|
+
validationNodes: number;
|
|
94
|
+
maxViolations: number;
|
|
95
|
+
valueBytes: number;
|
|
96
|
+
defaultRepairTurns: number;
|
|
97
|
+
maxRepairTurns: number;
|
|
98
|
+
ajv: boolean;
|
|
99
|
+
};
|
|
29
100
|
}
|
|
30
101
|
export declare function buildInfo(flags: CliFlags, version: string): ExecInfo;
|
package/dist/diagnostics/info.js
CHANGED
|
@@ -15,8 +15,15 @@ import { readFileSync } from 'node:fs';
|
|
|
15
15
|
import process from 'node:process';
|
|
16
16
|
import { getConfigPath, getSessionsDir, getUserDataDir } from '../config/app-paths.js';
|
|
17
17
|
import { loadConfig, makeGetApiKey } from '../config/load.js';
|
|
18
|
-
import { HOST_TOOL_NAMES } from '../tools/index.js';
|
|
19
|
-
import {
|
|
18
|
+
import { CALLER_GATED_TOOLS, HOST_TOOL_NAMES } from '../tools/index.js';
|
|
19
|
+
import { BRIDGE_CAPABILITIES, BRIDGE_PROTOCOL_VERSION } from '../exec/bridge/protocol.js';
|
|
20
|
+
import { BRIDGE_LIMITS } from '../exec/bridge/limits.js';
|
|
21
|
+
import { EXEC_SCHEMA_VERSION, execCapabilities } from '../exec/events.js';
|
|
22
|
+
import { RESULT_LIMITS } from '../exec/result/limits.js';
|
|
23
|
+
import { REQUEST_LIMITS } from '../exec/request/limits.js';
|
|
24
|
+
import { THREAD_LIMITS } from '../exec/threads/limits.js';
|
|
25
|
+
import { HOOK_LIMITS, HOOK_POINTS } from '../exec/hooks/limits.js';
|
|
26
|
+
import { hasAjvValidator } from './doctor.js';
|
|
20
27
|
/**
|
|
21
28
|
* Everything this build can do, as names a wrapper can test for.
|
|
22
29
|
*
|
|
@@ -52,11 +59,90 @@ export function buildInfo(flags, version) {
|
|
|
52
59
|
// `HOST_TOOL_NAMES` means and exactly what `--allow-tool` validates against.
|
|
53
60
|
tools: [...HOST_TOOL_NAMES],
|
|
54
61
|
outputFormats: ['text', 'json', 'stream-json'],
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
// THESE TWO LINES ARE THE ANSWER TO "WHAT DOES THIS BUILD ACCEPT", and this is
|
|
63
|
+
// the only place that answer exists in machine-readable form. Leaving them at
|
|
64
|
+
// three and three while `system/init` announces `bridge` and a run accepts
|
|
65
|
+
// `--permission-mode ask` would be two machine-readable answers that contradict
|
|
66
|
+
// each other, with nothing to tell a caller which one is true.
|
|
67
|
+
inputFormats: ['text', 'stream-json', 'bridge'],
|
|
68
|
+
permissionModes: ['auto', 'plan', 'strict', 'ask'],
|
|
69
|
+
features: buildFeatures(),
|
|
70
|
+
bridge: {
|
|
71
|
+
protocolVersion: BRIDGE_PROTOCOL_VERSION,
|
|
72
|
+
capabilities: [...BRIDGE_CAPABILITIES],
|
|
73
|
+
decisionKinds: ['tool', 'questions', 'plan', 'skill_install'],
|
|
74
|
+
// FROM THE CONSTANT THE GATE USES, so a caller never has to read the docs to
|
|
75
|
+
// learn that `skill_create` also asks it - and so this list cannot drift from
|
|
76
|
+
// the one enforcement is built on.
|
|
77
|
+
gatedTools: [...CALLER_GATED_TOOLS],
|
|
78
|
+
limits: {
|
|
79
|
+
maxCallerTools: BRIDGE_LIMITS.maxCallerTools,
|
|
80
|
+
maxFrameBytes: BRIDGE_LIMITS.maxFrameBytes,
|
|
81
|
+
defaultDecisionTimeoutMs: BRIDGE_LIMITS.defaultDecisionTimeoutMs,
|
|
82
|
+
},
|
|
83
|
+
requestLimits: {
|
|
84
|
+
maxRequestTurns: REQUEST_LIMITS.maxRequestTurns,
|
|
85
|
+
maxRequestTimeoutMs: REQUEST_LIMITS.maxRequestTimeoutMs,
|
|
86
|
+
},
|
|
87
|
+
// THREADS AND HOOKS (headless-threads-and-lifecycle-hooks section 4.6). REFERENCED
|
|
88
|
+
// from `THREAD_LIMITS` / `HOOK_LIMITS`, never restated: this block and the runtime's
|
|
89
|
+
// clamps have to be the same numbers, and a second literal is how they stop being.
|
|
90
|
+
threadLimits: {
|
|
91
|
+
maxOpenThreads: THREAD_LIMITS.maxOpenThreads,
|
|
92
|
+
defaultMaxOpenThreads: THREAD_LIMITS.defaultMaxOpenThreads,
|
|
93
|
+
defaultConcurrency: THREAD_LIMITS.defaultConcurrency,
|
|
94
|
+
maxThreadIdLength: THREAD_LIMITS.maxThreadIdLength,
|
|
95
|
+
},
|
|
96
|
+
hookLimits: {
|
|
97
|
+
maxHooks: HOOK_LIMITS.maxHooks,
|
|
98
|
+
maxHooksPerPoint: HOOK_LIMITS.maxHooksPerPoint,
|
|
99
|
+
defaultTimeoutMs: HOOK_LIMITS.defaultTimeoutMs,
|
|
100
|
+
minTimeoutMs: HOOK_LIMITS.minTimeoutMs,
|
|
101
|
+
maxTimeoutMs: HOOK_LIMITS.maxTimeoutMs,
|
|
102
|
+
allowModeMaxTimeoutMs: HOOK_LIMITS.allowModeMaxTimeoutMs,
|
|
103
|
+
points: [...HOOK_POINTS],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
result: {
|
|
107
|
+
schemaBytes: RESULT_LIMITS.maxSchemaBytes,
|
|
108
|
+
schemaDepth: RESULT_LIMITS.maxSchemaDepth,
|
|
109
|
+
schemaRefs: RESULT_LIMITS.maxSchemaRefs,
|
|
110
|
+
validationNodes: RESULT_LIMITS.maxValidationNodes,
|
|
111
|
+
maxViolations: RESULT_LIMITS.maxViolations,
|
|
112
|
+
valueBytes: RESULT_LIMITS.maxResultValueBytes,
|
|
113
|
+
defaultRepairTurns: RESULT_LIMITS.defaultRepairTurns,
|
|
114
|
+
maxRepairTurns: RESULT_LIMITS.maxRepairTurns,
|
|
115
|
+
ajv: hasAjvValidator(),
|
|
116
|
+
},
|
|
58
117
|
};
|
|
59
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* `features` = this build's own names PLUS the capability strings of one
|
|
121
|
+
* fully-featured run.
|
|
122
|
+
*
|
|
123
|
+
* DERIVED FROM `execCapabilities()`, NOT WRITTEN OUT BY HAND (review P0-4). The whole
|
|
124
|
+
* point of `info` is that a caller can read it BEFORE deciding which frames to send;
|
|
125
|
+
* if this list and `system/init.capabilities` were two hand-maintained literals, the
|
|
126
|
+
* first one to gain a member would silently make that strategy wrong.
|
|
127
|
+
* `{ bridge: true, structuredResult: true }` is the right argument here because this
|
|
128
|
+
* command answers "what can this build do", not "what did one run do".
|
|
129
|
+
*/
|
|
130
|
+
function buildFeatures() {
|
|
131
|
+
const features = [...FEATURES];
|
|
132
|
+
for (const capability of execCapabilities({
|
|
133
|
+
bridge: true,
|
|
134
|
+
structuredResult: true,
|
|
135
|
+
// THE SUPERSET ARGUMENT AGAIN: `info` answers "what can this build do", so the two new
|
|
136
|
+
// capabilities are declared here and only HERE - a run that does not open `--threads`
|
|
137
|
+
// still reports them in its own `init` as absent, from the same derivation point.
|
|
138
|
+
threads: true,
|
|
139
|
+
hooks: true,
|
|
140
|
+
})) {
|
|
141
|
+
if (!features.includes(capability))
|
|
142
|
+
features.push(capability);
|
|
143
|
+
}
|
|
144
|
+
return features;
|
|
145
|
+
}
|
|
60
146
|
/**
|
|
61
147
|
* The installed `@aragon-agent/core` version, or `'unknown'`.
|
|
62
148
|
*
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caller-served tools: what a `tool_register` frame becomes, and how it runs.
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY - `src/exec/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* ## I-12: THESE TOOLS LIVE OUTSIDE THE HOST'S JURISDICTION, ON PURPOSE
|
|
7
|
+
*
|
|
8
|
+
* Everything below is built HERE and handed straight to the registry. That means a
|
|
9
|
+
* caller tool is NOT covered by the plan gate, NOT covered by a skill's
|
|
10
|
+
* `allowed-tools` ceiling, and has no job-notice tail - because it never passes
|
|
11
|
+
* through `createBuiltinTools`, which is where all three wrappers are applied.
|
|
12
|
+
*
|
|
13
|
+
* This is a decision, not an oversight, and it is written down because the next
|
|
14
|
+
* person to read this file will otherwise assume the wrappers are there. The
|
|
15
|
+
* defences that DO apply are: plan mode refuses the whole `tool_register` frame
|
|
16
|
+
* (`plan_mode_read_only`), and a caller tool may not shadow any host, team or
|
|
17
|
+
* skill tool name (`caller_tool_conflict`). Both are refusals at registration
|
|
18
|
+
* time, which is the only moment this module can still see the caller's intent.
|
|
19
|
+
*
|
|
20
|
+
* ## Why `validateCallerTools` REFUSES WHOLE FRAMES
|
|
21
|
+
*
|
|
22
|
+
* A partially accepted registration would leave the registry holding a mixture of
|
|
23
|
+
* two declarations - the caller's old `db_query` and its new `db_write` - with
|
|
24
|
+
* nothing anywhere saying which of the two the model is now using. The caller's
|
|
25
|
+
* natural recovery ("just send the fixed list again") only works if the previous
|
|
26
|
+
* frame left no trace, so every check below runs BEFORE the first write.
|
|
27
|
+
*/
|
|
28
|
+
import { type AgentTool, type ToolResult } from '@aragon-agent/core';
|
|
29
|
+
import type { BridgeServer } from './server.js';
|
|
30
|
+
import type { CallerToolSpec } from './protocol.js';
|
|
31
|
+
export interface CallerToolValidation {
|
|
32
|
+
ok: true;
|
|
33
|
+
specs: CallerToolSpec[];
|
|
34
|
+
/** Non-fatal notes for `ack.warnings` - a clipped description, so far. */
|
|
35
|
+
warnings: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface CallerToolRejection {
|
|
38
|
+
ok: false;
|
|
39
|
+
code: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
export type CallerToolValidationResult = CallerToolValidation | CallerToolRejection;
|
|
43
|
+
/**
|
|
44
|
+
* Narrow whatever arrived in `tool_register.tools` into usable specs.
|
|
45
|
+
*
|
|
46
|
+
* `reserved` is a PARAMETER rather than an import so the caller decides what a
|
|
47
|
+
* name may not collide with - and so the test can hand over a two-name set instead
|
|
48
|
+
* of modelling the whole host.
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateCallerTools(rawTools: readonly unknown[], reserved: ReadonlySet<string>): CallerToolValidationResult;
|
|
51
|
+
/**
|
|
52
|
+
* The effective ceiling for one call: `min(spec.timeoutMs, --caller-tool-timeout)`.
|
|
53
|
+
*
|
|
54
|
+
* MIN, NOT MAX, because both numbers are ceilings on the same thing: the caller's
|
|
55
|
+
* own declaration is a promise about its implementation ("this query takes at most
|
|
56
|
+
* 20 s") and the flag is the run's budget. Taking the larger of the two would let
|
|
57
|
+
* a declared 600 s override a 10 s budget.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveCallerToolTimeout(spec: CallerToolSpec | undefined, defaultTimeoutMs: number): number;
|
|
60
|
+
/**
|
|
61
|
+
* Turn specs into `AgentTool`s that round-trip through the server.
|
|
62
|
+
*
|
|
63
|
+
* EVERY PATH RETURNS A `ToolResult`. Nothing here throws: `ToolExecutor` would
|
|
64
|
+
* convert a throw into an error result of its own wording, and that wording says
|
|
65
|
+
* nothing about which of the four refusals happened.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createCallerTools(specs: readonly CallerToolSpec[], server: BridgeServer, defaultTimeoutMs: number): AgentTool[];
|
|
68
|
+
/**
|
|
69
|
+
* Coerce a `tool_result.content` into text, or refuse it.
|
|
70
|
+
*
|
|
71
|
+
* TEXT ONLY, THIS ROUND. An image part would need `imageResult` and the provider's
|
|
72
|
+
* multimodal path, which is a separate feature with its own budget questions; a
|
|
73
|
+
* half-support that silently dropped the image would be worse than a named
|
|
74
|
+
* refusal (`bridge_unsupported_content`).
|
|
75
|
+
*/
|
|
76
|
+
export declare function contentToText(content: unknown): {
|
|
77
|
+
ok: true;
|
|
78
|
+
text: string;
|
|
79
|
+
} | {
|
|
80
|
+
ok: false;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Clip to `maxCallerToolResultBytes` and SAY SO.
|
|
84
|
+
*
|
|
85
|
+
* The footer is not decoration: `ToolExecutor` already clips model-visible output
|
|
86
|
+
* at its own ceiling, so a silent second clip here would look like the tool
|
|
87
|
+
* returned less than it did, and the model would draw conclusions from a truncated
|
|
88
|
+
* table without being told it was truncated.
|
|
89
|
+
*/
|
|
90
|
+
export declare function clipCallerToolText(text: string): string;
|
|
91
|
+
/** Build the result a `tool_result` frame becomes, or a named refusal. */
|
|
92
|
+
export declare function callerToolResult(content: unknown, isError: boolean): ToolResult;
|
|
93
|
+
/** Thrown only so `server.ts` can map it to one error code in one place. */
|
|
94
|
+
export declare class UnsupportedContentError extends Error {
|
|
95
|
+
constructor();
|
|
96
|
+
}
|