@arcforge/err 2.0.120 → 2.0.122
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/package.json +2 -2
- package/src/err.ts +8 -2
- package/src/map.ts +294 -6
- package/src/render.ts +29 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcforge/err",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.122",
|
|
4
4
|
"description": "Structured Axon errors — the code map every user-facing failure is rendered from.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"deploy": "echo \"This package is published ONLY by apps/tui/scripts/release.ts, which pins workspace:* deps to concrete versions first. Publishing it directly ships an unresolvable dependency.\" && exit 1"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@arcforge/types": "2.0.
|
|
18
|
+
"@arcforge/types": "2.0.122"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/bun": "latest",
|
package/src/err.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errorMap, type AxonErrorCode, type AxonErrorMap } from "./map"
|
|
1
|
+
import { errorMap, type AxonErrorCode, type AxonErrorMap, type AxonErrorMapEntry } from "./map"
|
|
2
2
|
import { captureStack, firstRealFrame } from "./stack"
|
|
3
3
|
import { renderError } from "./render"
|
|
4
4
|
import { emitError } from "./sink"
|
|
@@ -56,7 +56,10 @@ export function err(codeOrCause: AxonErrorCode | unknown, opts?: AxonErrorOpts):
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const code = codeOrCause as AxonErrorCode
|
|
59
|
-
|
|
59
|
+
// Read through the ENTRY type, not the literal map type: `as const` keeps
|
|
60
|
+
// each entry's exact shape, so optional fields are absent from the type of
|
|
61
|
+
// entries that omit them and unreadable off the union.
|
|
62
|
+
const def: AxonErrorMapEntry = (errorMap as AxonErrorMap)[code]
|
|
60
63
|
|
|
61
64
|
const e = new Error(opts?.detail ?? def.title, { cause: opts?.cause }) as AxonError
|
|
62
65
|
|
|
@@ -65,6 +68,9 @@ export function err(codeOrCause: AxonErrorCode | unknown, opts?: AxonErrorOpts):
|
|
|
65
68
|
e.description = def.description
|
|
66
69
|
e.source = def.source
|
|
67
70
|
e.severity = opts?.severity ?? def.severity
|
|
71
|
+
// Carried onto the instance so renderers (and the CLI's own catch) can ask
|
|
72
|
+
// whose fault this is without reaching back into the map.
|
|
73
|
+
if (def.expected) e.expected = true
|
|
68
74
|
e.context = opts?.context
|
|
69
75
|
e.frames = captureStack(2) // drop captureStack's own frame + err()'s
|
|
70
76
|
e.isAxonError = true
|
package/src/map.ts
CHANGED
|
@@ -33,6 +33,25 @@ export type AxonErrorMapEntry = {
|
|
|
33
33
|
description: string
|
|
34
34
|
source: AxonErrorSource
|
|
35
35
|
severity: AxonErrorSeverity
|
|
36
|
+
/**
|
|
37
|
+
* Is this a failure the USER caused and can fix, rather than a fault in
|
|
38
|
+
* our code?
|
|
39
|
+
*
|
|
40
|
+
* `axon publish` outside a project directory is a typo, not a bug — the
|
|
41
|
+
* title and description already say everything actionable, so a renderer
|
|
42
|
+
* showing eighty lines of our internals underneath is telling the user to
|
|
43
|
+
* debug software they did not write. Marking it `expected` renders the
|
|
44
|
+
* headline alone.
|
|
45
|
+
*
|
|
46
|
+
* Deliberately NOT `severity`, which answers a different question:
|
|
47
|
+
* PROJECT_NOT_FOUND is genuinely `fatal` (the command cannot continue) AND
|
|
48
|
+
* genuinely expected. Recoverability and blame are independent axes.
|
|
49
|
+
*
|
|
50
|
+
* Absent means unexpected — the safe default. An unclassified failure gets
|
|
51
|
+
* the full trace, which is what makes it debuggable; the only cost of
|
|
52
|
+
* forgetting this flag is a noisier message, never a hidden bug.
|
|
53
|
+
*/
|
|
54
|
+
expected?: true
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
/**
|
|
@@ -52,6 +71,7 @@ export const errorMap = {
|
|
|
52
71
|
description: "The requested prompt could not be resolved — it is not declared by the agent, or the published package it names ships no prompt by that name.",
|
|
53
72
|
source: "runtime",
|
|
54
73
|
severity: "fatal",
|
|
74
|
+
expected: true,
|
|
55
75
|
},
|
|
56
76
|
PROMPT_FILE_NOT_FOUND: {
|
|
57
77
|
code: "AX-PROMPT-002",
|
|
@@ -59,6 +79,7 @@ export const errorMap = {
|
|
|
59
79
|
description: "The prompt is declared but its source file no longer exists on disk.",
|
|
60
80
|
source: "runtime",
|
|
61
81
|
severity: "fatal",
|
|
82
|
+
expected: true,
|
|
62
83
|
},
|
|
63
84
|
PROMPT_RENDER_FAILED: {
|
|
64
85
|
code: "AX-PROMPT-003",
|
|
@@ -87,6 +108,7 @@ export const errorMap = {
|
|
|
87
108
|
description: "The requested script was not found in the agent's blueprint. It may not be declared, or the agent needs to be re-prepared.",
|
|
88
109
|
source: "runtime",
|
|
89
110
|
severity: "fatal",
|
|
111
|
+
expected: true,
|
|
90
112
|
},
|
|
91
113
|
SCRIPT_FILE_NOT_FOUND: {
|
|
92
114
|
code: "AX-SCRIPT-002",
|
|
@@ -94,6 +116,7 @@ export const errorMap = {
|
|
|
94
116
|
description: "The script is declared but its source file no longer exists on disk.",
|
|
95
117
|
source: "runtime",
|
|
96
118
|
severity: "fatal",
|
|
119
|
+
expected: true,
|
|
97
120
|
},
|
|
98
121
|
ENGINE_MISSING: {
|
|
99
122
|
code: "AX-ENGINE-001",
|
|
@@ -236,6 +259,7 @@ export const errorMap = {
|
|
|
236
259
|
description: "The cognet named by `cognet:` in axon.config.ts could not be located. The detail says where it was looked for — a registry that answered 404, or a node_modules tree it was missing from. Check the specifier for a typo, and check which registry the CLI is pointed at (AXON_API_BASE).",
|
|
237
260
|
source: "cognet",
|
|
238
261
|
severity: "fatal",
|
|
262
|
+
expected: true,
|
|
239
263
|
},
|
|
240
264
|
|
|
241
265
|
// ── Project (build/project/) ────────────────────────────────────────────
|
|
@@ -303,6 +327,7 @@ export const errorMap = {
|
|
|
303
327
|
description: "The selected model routes through a provider (OpenRouter, Codex) that isn't connected yet — connect it before picking a model on that route.",
|
|
304
328
|
source: "tui",
|
|
305
329
|
severity: "fatal",
|
|
330
|
+
expected: true,
|
|
306
331
|
},
|
|
307
332
|
PALETTE_INPUT_REQUIRED: {
|
|
308
333
|
code: "AX-TUI-012",
|
|
@@ -310,6 +335,7 @@ export const errorMap = {
|
|
|
310
335
|
description: "A palette command needs a value for this field before it can run.",
|
|
311
336
|
source: "tui",
|
|
312
337
|
severity: "fatal",
|
|
338
|
+
expected: true,
|
|
313
339
|
},
|
|
314
340
|
BENCH_NO_CASES: {
|
|
315
341
|
code: "AX-TUI-013",
|
|
@@ -326,6 +352,7 @@ export const errorMap = {
|
|
|
326
352
|
description: "No axon.config.ts, module.config.ts, cognet.config.ts, bench.config.ts, or prompt.config.ts was found at this path — it isn't a recognized project directory.",
|
|
327
353
|
source: "manifest",
|
|
328
354
|
severity: "fatal",
|
|
355
|
+
expected: true,
|
|
329
356
|
},
|
|
330
357
|
PROJECT_WRONG_KIND: {
|
|
331
358
|
code: "AX-PROJECT-011",
|
|
@@ -333,6 +360,7 @@ export const errorMap = {
|
|
|
333
360
|
description: "This command targets one project kind, and the directory holds another — run the command that matches what is actually here.",
|
|
334
361
|
source: "manifest",
|
|
335
362
|
severity: "fatal",
|
|
363
|
+
expected: true,
|
|
336
364
|
},
|
|
337
365
|
PROJECT_EXISTS: {
|
|
338
366
|
code: "AX-PROJECT-018",
|
|
@@ -357,8 +385,8 @@ export const errorMap = {
|
|
|
357
385
|
},
|
|
358
386
|
PUBLISH_UNSUPPORTED_KIND: {
|
|
359
387
|
code: "AX-PROJECT-009",
|
|
360
|
-
title: "This Project Kind Cannot Publish
|
|
361
|
-
description: "
|
|
388
|
+
title: "This Project Kind Cannot Publish",
|
|
389
|
+
description: "The registry accepts agents, modules, cognets, benches and prompts. Extensions are not accepted yet — the `registry_artifact_kind` enum needs the value first, and publishing before then would register under the wrong kind and claim the name in the shared namespace. A profile is never publishable: it holds one person's credentials, history and agents. See KINDS[kind].publishable.",
|
|
362
390
|
source: "manifest",
|
|
363
391
|
severity: "fatal",
|
|
364
392
|
},
|
|
@@ -412,14 +440,14 @@ export const errorMap = {
|
|
|
412
440
|
severity: "fatal",
|
|
413
441
|
},
|
|
414
442
|
DEPLOY_RUNTIME_FAILED: {
|
|
415
|
-
code: "AX-PROJECT-
|
|
443
|
+
code: "AX-PROJECT-035",
|
|
416
444
|
title: "Agent Failed To Start",
|
|
417
445
|
description: "Cloud infrastructure was provisioned, but the agent process failed during boot. The reported runtime diagnostic identifies the immediate cause.",
|
|
418
446
|
source: "runtime",
|
|
419
447
|
severity: "fatal",
|
|
420
448
|
},
|
|
421
449
|
DEPLOY_ENV_RESERVED: {
|
|
422
|
-
code: "AX-PROJECT-
|
|
450
|
+
code: "AX-PROJECT-036",
|
|
423
451
|
title: "Reserved Deployment Variable",
|
|
424
452
|
description: "The production .env file attempts to override a variable owned by the Axon runtime or Cloud Run.",
|
|
425
453
|
source: "manifest",
|
|
@@ -468,6 +496,7 @@ export const errorMap = {
|
|
|
468
496
|
description: "No recorded bench run exists with this id.",
|
|
469
497
|
source: "bench",
|
|
470
498
|
severity: "fatal",
|
|
499
|
+
expected: true,
|
|
471
500
|
},
|
|
472
501
|
BENCH_NOT_FOUND: {
|
|
473
502
|
code: "AX-BENCH-002",
|
|
@@ -475,6 +504,7 @@ export const errorMap = {
|
|
|
475
504
|
description: "No bench.config.ts was found at this path — it isn't a recognized bench project.",
|
|
476
505
|
source: "bench",
|
|
477
506
|
severity: "fatal",
|
|
507
|
+
expected: true,
|
|
478
508
|
},
|
|
479
509
|
BENCH_TESTS_NOT_FOUND: {
|
|
480
510
|
code: "AX-BENCH-003",
|
|
@@ -482,6 +512,7 @@ export const errorMap = {
|
|
|
482
512
|
description: "The bench config declares test files that don't exist on disk.",
|
|
483
513
|
source: "bench",
|
|
484
514
|
severity: "fatal",
|
|
515
|
+
expected: true,
|
|
485
516
|
},
|
|
486
517
|
BENCH_LOCAL_REF_NOT_FOUND: {
|
|
487
518
|
code: "AX-BENCH-004",
|
|
@@ -489,6 +520,7 @@ export const errorMap = {
|
|
|
489
520
|
description: "A factor variable references a local file/directory that doesn't exist.",
|
|
490
521
|
source: "bench",
|
|
491
522
|
severity: "fatal",
|
|
523
|
+
expected: true,
|
|
492
524
|
},
|
|
493
525
|
BENCH_CONFIG_INVALID: {
|
|
494
526
|
code: "AX-BENCH-005",
|
|
@@ -503,6 +535,7 @@ export const errorMap = {
|
|
|
503
535
|
description: "No package.json exists at the bench root — every bench project needs one for identity.",
|
|
504
536
|
source: "bench",
|
|
505
537
|
severity: "fatal",
|
|
538
|
+
expected: true,
|
|
506
539
|
},
|
|
507
540
|
BENCH_PACKAGE_NAME_REQUIRED: {
|
|
508
541
|
code: "AX-BENCH-007",
|
|
@@ -510,6 +543,7 @@ export const errorMap = {
|
|
|
510
543
|
description: "The bench's package.json has no name field.",
|
|
511
544
|
source: "bench",
|
|
512
545
|
severity: "fatal",
|
|
546
|
+
expected: true,
|
|
513
547
|
},
|
|
514
548
|
BENCH_PACKAGE_VERSION_REQUIRED: {
|
|
515
549
|
code: "AX-BENCH-008",
|
|
@@ -517,6 +551,7 @@ export const errorMap = {
|
|
|
517
551
|
description: "The bench's package.json has no version field.",
|
|
518
552
|
source: "bench",
|
|
519
553
|
severity: "fatal",
|
|
554
|
+
expected: true,
|
|
520
555
|
},
|
|
521
556
|
BENCH_CONTEXT_MISSING: {
|
|
522
557
|
code: "AX-BENCH-009",
|
|
@@ -636,6 +671,7 @@ export const errorMap = {
|
|
|
636
671
|
description: "The bench config's matrix has no axis with this key.",
|
|
637
672
|
source: "bench",
|
|
638
673
|
severity: "fatal",
|
|
674
|
+
expected: true,
|
|
639
675
|
},
|
|
640
676
|
BENCH_AXIS_VALUE_NOT_FOUND: {
|
|
641
677
|
code: "AX-BENCH-022",
|
|
@@ -643,6 +679,7 @@ export const errorMap = {
|
|
|
643
679
|
description: "The bench config's matrix axis has no declared value with this id.",
|
|
644
680
|
source: "bench",
|
|
645
681
|
severity: "fatal",
|
|
682
|
+
expected: true,
|
|
646
683
|
},
|
|
647
684
|
BENCH_WORKSPACE_ESCAPE: {
|
|
648
685
|
code: "AX-BENCH-023",
|
|
@@ -671,6 +708,7 @@ export const errorMap = {
|
|
|
671
708
|
description: "A workspace template's declared source path isn't a real directory.",
|
|
672
709
|
source: "bench",
|
|
673
710
|
severity: "fatal",
|
|
711
|
+
expected: true,
|
|
674
712
|
},
|
|
675
713
|
BENCH_LOG_INVALID: {
|
|
676
714
|
code: "AX-BENCH-027",
|
|
@@ -736,6 +774,7 @@ export const errorMap = {
|
|
|
736
774
|
description: "No axon.config.ts exists at this path.",
|
|
737
775
|
source: "manifest",
|
|
738
776
|
severity: "fatal",
|
|
777
|
+
expected: true,
|
|
739
778
|
},
|
|
740
779
|
CONFIG_LOAD_FAILED: {
|
|
741
780
|
code: "AX-BLUEPRINT-004",
|
|
@@ -773,6 +812,7 @@ export const errorMap = {
|
|
|
773
812
|
description: "This agent uses a Codex model, but your Axon account is not connected to ChatGPT — run :provider codex connect and try again.",
|
|
774
813
|
source: "kernel",
|
|
775
814
|
severity: "fatal",
|
|
815
|
+
expected: true,
|
|
776
816
|
},
|
|
777
817
|
RUN_IN_PROGRESS: {
|
|
778
818
|
code: "AX-KERNEL-002",
|
|
@@ -838,6 +878,7 @@ export const errorMap = {
|
|
|
838
878
|
description: "The requested thread id isn't registered in this session.",
|
|
839
879
|
source: "thread",
|
|
840
880
|
severity: "fatal",
|
|
881
|
+
expected: true,
|
|
841
882
|
},
|
|
842
883
|
THREAD_BRANCH_UNKNOWN: {
|
|
843
884
|
code: "AX-SESSION-003",
|
|
@@ -873,6 +914,7 @@ export const errorMap = {
|
|
|
873
914
|
description: "A control-channel call named a method the peer does not expose. The two ends disagree about the surface — usually a TUI and an extension built from different versions.",
|
|
874
915
|
source: "server",
|
|
875
916
|
severity: "fatal",
|
|
917
|
+
expected: true,
|
|
876
918
|
},
|
|
877
919
|
CONTROL_PATH_NOT_CALLABLE: {
|
|
878
920
|
code: "AX-CONTROL-002",
|
|
@@ -1096,6 +1138,7 @@ export const errorMap = {
|
|
|
1096
1138
|
description: "The action needs an active profile, but none is logged in yet.",
|
|
1097
1139
|
source: "tui",
|
|
1098
1140
|
severity: "fatal",
|
|
1141
|
+
expected: true,
|
|
1099
1142
|
},
|
|
1100
1143
|
PROFILE_NOT_AUTHENTICATED: {
|
|
1101
1144
|
code: "AX-TUI-003",
|
|
@@ -1103,6 +1146,15 @@ export const errorMap = {
|
|
|
1103
1146
|
description: "The target profile exists but has no stored session — it has never logged in, or its session was cleared.",
|
|
1104
1147
|
source: "tui",
|
|
1105
1148
|
severity: "fatal",
|
|
1149
|
+
expected: true,
|
|
1150
|
+
},
|
|
1151
|
+
BACKEND_UNREACHABLE: {
|
|
1152
|
+
code: "AX-TUI-044",
|
|
1153
|
+
title: "Backend Unreachable",
|
|
1154
|
+
description: "A stored credential could not be verified because the backend could not be reached. The credential is NOT discarded — it may be perfectly valid — but it cannot be trusted until it is checked, so the action is refused rather than proceeding unverified.",
|
|
1155
|
+
source: "tui",
|
|
1156
|
+
severity: "fatal",
|
|
1157
|
+
expected: true,
|
|
1106
1158
|
},
|
|
1107
1159
|
PROFILE_UNKNOWN: {
|
|
1108
1160
|
code: "AX-TUI-004",
|
|
@@ -1110,6 +1162,7 @@ export const errorMap = {
|
|
|
1110
1162
|
description: "The requested profile id isn't one of the profiles stored on this machine.",
|
|
1111
1163
|
source: "tui",
|
|
1112
1164
|
severity: "fatal",
|
|
1165
|
+
expected: true,
|
|
1113
1166
|
},
|
|
1114
1167
|
SESSION_ALREADY_RUNNING: {
|
|
1115
1168
|
code: "AX-TUI-005",
|
|
@@ -1117,6 +1170,7 @@ export const errorMap = {
|
|
|
1117
1170
|
description: "spawn() was asked to resume a session that already has a live instance. Focus the running instance instead of booting a second runtime over the same log.",
|
|
1118
1171
|
source: "tui",
|
|
1119
1172
|
severity: "fatal",
|
|
1173
|
+
expected: true,
|
|
1120
1174
|
},
|
|
1121
1175
|
SESSION_NOT_RUNNING: {
|
|
1122
1176
|
code: "AX-TUI-016",
|
|
@@ -1124,6 +1178,15 @@ export const errorMap = {
|
|
|
1124
1178
|
description: "focus() was pointed at a sessionId with no live instance behind it. Spawn (or resume) it first — focus is pure selection over running instances.",
|
|
1125
1179
|
source: "tui",
|
|
1126
1180
|
severity: "fatal",
|
|
1181
|
+
expected: true,
|
|
1182
|
+
},
|
|
1183
|
+
NO_ACTIVE_AGENT: {
|
|
1184
|
+
code: "AX-TUI-045",
|
|
1185
|
+
title: "No Active Agent",
|
|
1186
|
+
description: "The action needs a running agent, but none is active — start one first.",
|
|
1187
|
+
source: "tui",
|
|
1188
|
+
severity: "fatal",
|
|
1189
|
+
expected: true,
|
|
1127
1190
|
},
|
|
1128
1191
|
NO_FOCUSED_INSTANCE: {
|
|
1129
1192
|
code: "AX-TUI-017",
|
|
@@ -1131,6 +1194,7 @@ export const errorMap = {
|
|
|
1131
1194
|
description: "A module install/uninstall was requested with no running agent instance focused — spawn or focus one first.",
|
|
1132
1195
|
source: "tui",
|
|
1133
1196
|
severity: "fatal",
|
|
1197
|
+
expected: true,
|
|
1134
1198
|
},
|
|
1135
1199
|
MODEL_IMMUTABLE_DEPLOYED: {
|
|
1136
1200
|
code: "AX-TUI-006",
|
|
@@ -1138,6 +1202,7 @@ export const errorMap = {
|
|
|
1138
1202
|
description: "A deployed agent's config lives in the cloud, not on this machine — its model is fixed at deploy time. Change it in the local project and deploy again.",
|
|
1139
1203
|
source: "tui",
|
|
1140
1204
|
severity: "fatal",
|
|
1205
|
+
expected: true,
|
|
1141
1206
|
},
|
|
1142
1207
|
MODULE_INSTALL_FAILED: {
|
|
1143
1208
|
code: "AX-TUI-018",
|
|
@@ -1215,6 +1280,7 @@ export const errorMap = {
|
|
|
1215
1280
|
description: "`axon watch`/`axon unwatch` need a directory argument.",
|
|
1216
1281
|
source: "tui",
|
|
1217
1282
|
severity: "fatal",
|
|
1283
|
+
expected: true,
|
|
1218
1284
|
},
|
|
1219
1285
|
WATCH_PATH_NOT_FOUND: {
|
|
1220
1286
|
code: "AX-TUI-022",
|
|
@@ -1222,6 +1288,7 @@ export const errorMap = {
|
|
|
1222
1288
|
description: "`axon watch` was given a directory that doesn't exist on disk.",
|
|
1223
1289
|
source: "tui",
|
|
1224
1290
|
severity: "fatal",
|
|
1291
|
+
expected: true,
|
|
1225
1292
|
},
|
|
1226
1293
|
EDITOR_NOT_SET: {
|
|
1227
1294
|
code: "AX-TUI-023",
|
|
@@ -1243,6 +1310,7 @@ export const errorMap = {
|
|
|
1243
1310
|
description: "The init palette's \"watch a new directory\" entry needs both a directory path and an agent name, space-separated.",
|
|
1244
1311
|
source: "tui",
|
|
1245
1312
|
severity: "fatal",
|
|
1313
|
+
expected: true,
|
|
1246
1314
|
},
|
|
1247
1315
|
|
|
1248
1316
|
// ── Module boot-time execution (core runs defineModule setup) ────────────
|
|
@@ -1268,7 +1336,7 @@ export const errorMap = {
|
|
|
1268
1336
|
severity: "fatal",
|
|
1269
1337
|
},
|
|
1270
1338
|
MODULE_SETUP_FAILED: {
|
|
1271
|
-
code: "AX-MODULE-
|
|
1339
|
+
code: "AX-MODULE-007",
|
|
1272
1340
|
title: "Module Setup Failed",
|
|
1273
1341
|
description: "A module's setup() threw during agent boot. Setup runs sequentially in blueprint order and a failure is total — no later module is wired, and the agent does not boot half-configured.",
|
|
1274
1342
|
source: "runtime",
|
|
@@ -1287,6 +1355,7 @@ export const errorMap = {
|
|
|
1287
1355
|
description: "A module's setup() called ctx.env.require() for a variable the agent's resolved environment does not provide. The module declares required env in module.config.ts; the agent must supply it (e.g. in .env).",
|
|
1288
1356
|
source: "runtime",
|
|
1289
1357
|
severity: "fatal",
|
|
1358
|
+
expected: true,
|
|
1290
1359
|
},
|
|
1291
1360
|
MODULE_POLICY_IMMUTABLE: {
|
|
1292
1361
|
code: "AX-MODULE-006",
|
|
@@ -1294,6 +1363,7 @@ export const errorMap = {
|
|
|
1294
1363
|
description: "A module's setup() called ctx.policy.update(). The resolved agent policy is authoritative and cannot be mutated at boot — declare policy needs statically in module.config.ts so the CLI reconciles them at install.",
|
|
1295
1364
|
source: "runtime",
|
|
1296
1365
|
severity: "fatal",
|
|
1366
|
+
expected: true,
|
|
1297
1367
|
},
|
|
1298
1368
|
|
|
1299
1369
|
// ── Registry retrieval (build/registry.ts) ──────────────────────────────
|
|
@@ -1303,6 +1373,7 @@ export const errorMap = {
|
|
|
1303
1373
|
description: "`axon clone` was run without an artifact to clone. Name one, for example: axon clone @axon/arxiv",
|
|
1304
1374
|
source: "cli",
|
|
1305
1375
|
severity: "fatal",
|
|
1376
|
+
expected: true,
|
|
1306
1377
|
},
|
|
1307
1378
|
FORK_REF_REQUIRED: {
|
|
1308
1379
|
code: "AX-PROJECT-023",
|
|
@@ -1370,7 +1441,7 @@ export const errorMap = {
|
|
|
1370
1441
|
severity: "fatal",
|
|
1371
1442
|
},
|
|
1372
1443
|
PROMPT_NOT_INSTALLABLE: {
|
|
1373
|
-
code: "AX-PROJECT-
|
|
1444
|
+
code: "AX-PROJECT-037",
|
|
1374
1445
|
title: "Prompts Are Not Installed Into Agents",
|
|
1375
1446
|
description: "A prompt is content, not a capability: it resolves from the global cache and renders on demand, so every agent can already use it without declaring anything. Installing one into an agent would put content through the code path — an ABI check, a node_modules link, an agent reload — for something that needs none of it.",
|
|
1376
1447
|
source: "manifest",
|
|
@@ -1545,6 +1616,223 @@ export const errorMap = {
|
|
|
1545
1616
|
source: "cli",
|
|
1546
1617
|
severity: "fatal",
|
|
1547
1618
|
},
|
|
1619
|
+
|
|
1620
|
+
// ── Extensions ──────────────────────────────────────────────────────────
|
|
1621
|
+
//
|
|
1622
|
+
// The TUI config surface: a profile's main.ts and plugins/, and the
|
|
1623
|
+
// extensions it loads. Almost every entry here is `expected` and
|
|
1624
|
+
// `degraded`, and both flags are deliberate.
|
|
1625
|
+
//
|
|
1626
|
+
// EXPECTED, because this is code the USER wrote. A stack trace through our
|
|
1627
|
+
// loader tells someone to debug software they did not write; the file and
|
|
1628
|
+
// the reason are the entire actionable content.
|
|
1629
|
+
//
|
|
1630
|
+
// DEGRADED, because a broken config must never cost someone their
|
|
1631
|
+
// terminal. One bad file disables itself and everything else still loads —
|
|
1632
|
+
// a user whose plugin has a typo needs a working Axon to go fix it in.
|
|
1633
|
+
// The one FATAL entry is PROFILE_CONFIG_INVALID, and only because it is
|
|
1634
|
+
// thrown by the CLI verbs that edit that file, where continuing would mean
|
|
1635
|
+
// writing over a config we could not read.
|
|
1636
|
+
|
|
1637
|
+
PROFILE_CONFIG_INVALID: {
|
|
1638
|
+
code: "AX-EXT-001",
|
|
1639
|
+
title: "profile.config.ts Did Not Call defineProfile()",
|
|
1640
|
+
description: "A profile's config must default-export defineProfile({ ... }). Without it there is no extension list to read, so nothing can be installed, enabled or disabled. The rest of the profile — main.ts and plugins/ — still loads.",
|
|
1641
|
+
source: "tui",
|
|
1642
|
+
severity: "fatal",
|
|
1643
|
+
expected: true,
|
|
1644
|
+
},
|
|
1645
|
+
PROFILE_CONFIG_FAILED: {
|
|
1646
|
+
code: "AX-EXT-002",
|
|
1647
|
+
title: "profile.config.ts Failed to Load",
|
|
1648
|
+
description: "The file threw while being evaluated, so its extension list could not be read and no extensions were loaded. The profile's own main.ts and plugins/ are unaffected — a typo in an extension list must not cost you the config you already had working.",
|
|
1649
|
+
source: "tui",
|
|
1650
|
+
severity: "degraded",
|
|
1651
|
+
expected: true,
|
|
1652
|
+
},
|
|
1653
|
+
EXTENSION_ENTRY_INVALID: {
|
|
1654
|
+
code: "AX-EXT-003",
|
|
1655
|
+
title: "Malformed Extension Entry",
|
|
1656
|
+
description: "An entry in profile.config.ts's `extensions` array is neither a string nor an object with a `source`. Accepted forms are \"@scope/name\" for a registry extension and \"./extensions/name\" for a local one.",
|
|
1657
|
+
source: "tui",
|
|
1658
|
+
severity: "degraded",
|
|
1659
|
+
expected: true,
|
|
1660
|
+
},
|
|
1661
|
+
EXTENSION_NOT_FOUND: {
|
|
1662
|
+
code: "AX-EXT-004",
|
|
1663
|
+
title: "Extension Not Found",
|
|
1664
|
+
description: "An extension listed in profile.config.ts is not on disk. A local path may have moved or been deleted; a registry name may never have been installed. Everything else in the list still loads.",
|
|
1665
|
+
source: "tui",
|
|
1666
|
+
severity: "degraded",
|
|
1667
|
+
expected: true,
|
|
1668
|
+
},
|
|
1669
|
+
EXTENSION_INSTALL_FAILED: {
|
|
1670
|
+
code: "AX-EXT-005",
|
|
1671
|
+
title: "Extension Install Failed",
|
|
1672
|
+
description: "A registry extension could not be fetched or prepared — most often no network, or a name that does not exist in the registry. It is skipped for this session and retried on the next launch.",
|
|
1673
|
+
source: "tui",
|
|
1674
|
+
severity: "degraded",
|
|
1675
|
+
expected: true,
|
|
1676
|
+
},
|
|
1677
|
+
EXTENSION_NOT_AN_EXTENSION: {
|
|
1678
|
+
code: "AX-EXT-006",
|
|
1679
|
+
title: "Not an Extension",
|
|
1680
|
+
description: "The directory has no extension.config.ts, which is the file that marks a directory as an extension. A path pointing at an agent, a module, or an ordinary folder is not loadable as one.",
|
|
1681
|
+
source: "tui",
|
|
1682
|
+
severity: "degraded",
|
|
1683
|
+
expected: true,
|
|
1684
|
+
},
|
|
1685
|
+
EXTENSION_LOAD_FAILED: {
|
|
1686
|
+
code: "AX-EXT-007",
|
|
1687
|
+
title: "Extension Failed to Load",
|
|
1688
|
+
description: "An extension's main.ts threw while it was being loaded, so whatever it had not yet registered is missing. Its commands, keys and palettes are removed and the rest of your config loads normally.",
|
|
1689
|
+
source: "tui",
|
|
1690
|
+
severity: "degraded",
|
|
1691
|
+
expected: true,
|
|
1692
|
+
},
|
|
1693
|
+
PROFILE_MAIN_FAILED: {
|
|
1694
|
+
code: "AX-EXT-008",
|
|
1695
|
+
title: "main.ts Failed to Load",
|
|
1696
|
+
description: "Your profile's main.ts threw while being evaluated. Anything it registered before the error survives; everything after it did not run. Plugins and extensions still load.",
|
|
1697
|
+
source: "tui",
|
|
1698
|
+
severity: "degraded",
|
|
1699
|
+
expected: true,
|
|
1700
|
+
},
|
|
1701
|
+
PLUGIN_FAILED: {
|
|
1702
|
+
code: "AX-EXT-009",
|
|
1703
|
+
title: "Plugin Failed to Load",
|
|
1704
|
+
description: "A file in plugins/ threw while being imported, so its hooks were not registered. Other plugins in the same folder are unaffected — each file is loaded independently.",
|
|
1705
|
+
source: "tui",
|
|
1706
|
+
severity: "degraded",
|
|
1707
|
+
expected: true,
|
|
1708
|
+
},
|
|
1709
|
+
HOOK_FAILED: {
|
|
1710
|
+
code: "AX-EXT-010",
|
|
1711
|
+
title: "Lifecycle Hook Failed",
|
|
1712
|
+
description: "A tui.hook() handler threw while the event it listens for was firing. For a notification hook nothing else is affected; for a gating hook (tui:boot, tui:shutdown) the operation continues anyway rather than leaving the terminal stuck.",
|
|
1713
|
+
source: "tui",
|
|
1714
|
+
severity: "degraded",
|
|
1715
|
+
expected: true,
|
|
1716
|
+
},
|
|
1717
|
+
HOOK_TIMED_OUT: {
|
|
1718
|
+
code: "AX-EXT-011",
|
|
1719
|
+
title: "Hook Blocked Too Long",
|
|
1720
|
+
description: "A gating hook (tui:boot or tui:shutdown) did not finish within its budget, so the TUI continued without waiting. A config must never be able to hang the terminal silently — whatever the handler was doing may not have completed.",
|
|
1721
|
+
source: "tui",
|
|
1722
|
+
severity: "degraded",
|
|
1723
|
+
expected: true,
|
|
1724
|
+
},
|
|
1725
|
+
COMMAND_NOT_FOUND: {
|
|
1726
|
+
code: "AX-EXT-012",
|
|
1727
|
+
title: "No Such Command",
|
|
1728
|
+
description: "commands.run() was given a path that resolves to nothing in the command tree. The path must name a leaf, spelled exactly as it appears in the `:` palette.",
|
|
1729
|
+
source: "tui",
|
|
1730
|
+
severity: "fatal",
|
|
1731
|
+
expected: true,
|
|
1732
|
+
},
|
|
1733
|
+
PALETTE_NOT_FOUND: {
|
|
1734
|
+
code: "AX-EXT-013",
|
|
1735
|
+
title: "No Such Palette",
|
|
1736
|
+
description: "palette.open() was given a name no palette was registered under. Palettes are created with palette.create(name, ...) — a name is only openable once its registration has run.",
|
|
1737
|
+
source: "tui",
|
|
1738
|
+
severity: "fatal",
|
|
1739
|
+
expected: true,
|
|
1740
|
+
},
|
|
1741
|
+
PALETTE_NAME_TAKEN: {
|
|
1742
|
+
code: "AX-EXT-014",
|
|
1743
|
+
title: "Palette Name Already Registered",
|
|
1744
|
+
description: "Two palettes tried to claim the same name. Names are how palette.open() addresses one, so they have to be unique — silently replacing the first would make whichever loaded last win, invisibly.",
|
|
1745
|
+
source: "tui",
|
|
1746
|
+
severity: "degraded",
|
|
1747
|
+
expected: true,
|
|
1748
|
+
},
|
|
1749
|
+
PALETTE_ALREADY_OPEN: {
|
|
1750
|
+
code: "AX-EXT-015",
|
|
1751
|
+
title: "A Palette Is Already Open",
|
|
1752
|
+
description: "Something tried to open a palette, or ask a question, while the user was already navigating one. Stealing it mid-navigation would drop whatever they were doing, so the call fails instead — check palette.isOpen first.",
|
|
1753
|
+
source: "tui",
|
|
1754
|
+
severity: "degraded",
|
|
1755
|
+
expected: true,
|
|
1756
|
+
},
|
|
1757
|
+
KEY_CHORD_UNBOUND: {
|
|
1758
|
+
code: "AX-EXT-022",
|
|
1759
|
+
title: "No Such Key Binding",
|
|
1760
|
+
description: "keys.send() was given a chord nothing is bound to — neither a mode key nor a registered binding. Sending it would do nothing at all, which is indistinguishable from a binding that ran and had no effect.",
|
|
1761
|
+
source: "tui",
|
|
1762
|
+
severity: "fatal",
|
|
1763
|
+
expected: true,
|
|
1764
|
+
},
|
|
1765
|
+
AGENT_SPAWN_NO_INSTANCE: {
|
|
1766
|
+
code: "AX-EXT-023",
|
|
1767
|
+
title: "Agent Started but No Instance Appeared",
|
|
1768
|
+
description: "A spawn reported success without producing a focused instance, so there is no id to hand back. This is a fault in Axon rather than in your config — the agent may or may not be running.",
|
|
1769
|
+
source: "tui",
|
|
1770
|
+
severity: "fatal",
|
|
1771
|
+
},
|
|
1772
|
+
PROFILE_CONFIG_MISSING: {
|
|
1773
|
+
code: "AX-EXT-025",
|
|
1774
|
+
title: "No profile.config.ts",
|
|
1775
|
+
description: "The profile has no config file to record the extension in. It is normally created on first launch — starting Axon once will scaffold it.",
|
|
1776
|
+
source: "tui",
|
|
1777
|
+
severity: "fatal",
|
|
1778
|
+
expected: true,
|
|
1779
|
+
},
|
|
1780
|
+
PROFILE_CONFIG_UNEDITABLE: {
|
|
1781
|
+
code: "AX-EXT-026",
|
|
1782
|
+
title: "profile.config.ts Could Not Be Edited",
|
|
1783
|
+
description: "The `extensions` array could not be located, or editing it would have produced invalid TypeScript. Nothing was written — this file is yours, and a botched edit costs your whole TUI config. Add or remove the entry by hand.",
|
|
1784
|
+
source: "tui",
|
|
1785
|
+
severity: "fatal",
|
|
1786
|
+
expected: true,
|
|
1787
|
+
},
|
|
1788
|
+
COMMAND_PATH_TAKEN: {
|
|
1789
|
+
code: "AX-EXT-020",
|
|
1790
|
+
title: "Command Already Registered",
|
|
1791
|
+
description: "Two sources registered the same command path. The first one loaded keeps it — your own config beats any extension, and between extensions the one listed earlier in profile.config.ts wins. Rename one of them.",
|
|
1792
|
+
source: "tui",
|
|
1793
|
+
severity: "degraded",
|
|
1794
|
+
expected: true,
|
|
1795
|
+
},
|
|
1796
|
+
KEY_CHORD_TAKEN: {
|
|
1797
|
+
code: "AX-EXT-021",
|
|
1798
|
+
title: "Key Chord Already Bound",
|
|
1799
|
+
description: "Two sources bound the same key chord. The first one loaded keeps it. A silently shadowed binding is unbearable to debug in someone else's config, so the second registration fails instead of replacing it.",
|
|
1800
|
+
source: "tui",
|
|
1801
|
+
severity: "degraded",
|
|
1802
|
+
expected: true,
|
|
1803
|
+
},
|
|
1804
|
+
MODE_KEY_TAKEN: {
|
|
1805
|
+
code: "AX-EXT-016",
|
|
1806
|
+
title: "Mode Key Already Bound",
|
|
1807
|
+
description: "A palette asked for a mode key that is already in use — either by a built-in mode or by another extension. A silently shadowed key is unbearable to debug in someone else's config, so the registration fails loudly instead.",
|
|
1808
|
+
source: "tui",
|
|
1809
|
+
severity: "degraded",
|
|
1810
|
+
expected: true,
|
|
1811
|
+
},
|
|
1812
|
+
MODE_UNKNOWN: {
|
|
1813
|
+
code: "AX-EXT-017",
|
|
1814
|
+
title: "No Such Mode",
|
|
1815
|
+
description: "mode.set() was given a name that is neither a built-in mode nor a registered palette. Doing nothing would look identical to a mode that simply does not render, so this fails instead.",
|
|
1816
|
+
source: "tui",
|
|
1817
|
+
severity: "fatal",
|
|
1818
|
+
expected: true,
|
|
1819
|
+
},
|
|
1820
|
+
NO_FOCUSED_AGENT: {
|
|
1821
|
+
code: "AX-EXT-018",
|
|
1822
|
+
title: "No Agent Focused",
|
|
1823
|
+
description: "A verb that acts on the focused agent — send, stop, reboot — was called while nothing was running. Start one with agents.spawn(name) or tui.nav(name) first.",
|
|
1824
|
+
source: "tui",
|
|
1825
|
+
severity: "fatal",
|
|
1826
|
+
expected: true,
|
|
1827
|
+
},
|
|
1828
|
+
INSTANCE_NOT_RUNNING: {
|
|
1829
|
+
code: "AX-EXT-019",
|
|
1830
|
+
title: "Instance Not Running",
|
|
1831
|
+
description: "An instance id was given for a conversation that is not live — it may already have been stopped, or it belongs to an earlier session. agents.list() returns what is currently running.",
|
|
1832
|
+
source: "tui",
|
|
1833
|
+
severity: "fatal",
|
|
1834
|
+
expected: true,
|
|
1835
|
+
},
|
|
1548
1836
|
} as const satisfies Record<string, AxonErrorMapEntry>
|
|
1549
1837
|
|
|
1550
1838
|
export type AxonErrorMap = typeof errorMap
|
package/src/render.ts
CHANGED
|
@@ -20,6 +20,8 @@ export type AxonErrorLike = {
|
|
|
20
20
|
context: Record<string, unknown> | undefined
|
|
21
21
|
frames: AxonStackFrame[]
|
|
22
22
|
cause?: unknown
|
|
23
|
+
/** The user caused this and can fix it — renders without our internals. See map.ts. */
|
|
24
|
+
expected?: boolean
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** One frame, Rust-style: file:line:col, then its captured source context with a caret. */
|
|
@@ -37,7 +39,23 @@ export function renderFrame(frame: AxonStackFrame): string {
|
|
|
37
39
|
|
|
38
40
|
const RULE_WIDTH = 80
|
|
39
41
|
|
|
40
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* The renderable report.
|
|
44
|
+
*
|
|
45
|
+
* Two shapes, chosen by whose fault the failure is:
|
|
46
|
+
*
|
|
47
|
+
* EXPECTED headline + description + context. `axon publish` outside a
|
|
48
|
+
* project is a typo; the description already says exactly what
|
|
49
|
+
* to do, and eighty lines of our call stack underneath tells the
|
|
50
|
+
* user to debug software they did not write.
|
|
51
|
+
*
|
|
52
|
+
* otherwise the full report — frames, source snippets, cause chain. An
|
|
53
|
+
* unclassified failure IS ours, and this is what makes it
|
|
54
|
+
* debuggable from a pasted terminal log.
|
|
55
|
+
*
|
|
56
|
+
* The default is the full report (see map.ts's `expected`), so forgetting to
|
|
57
|
+
* classify a code costs a noisy message and never a hidden bug.
|
|
58
|
+
*/
|
|
41
59
|
export function renderError(error: AxonErrorLike): string {
|
|
42
60
|
const lines = [`Axon Error: ${error.title}`, error.description]
|
|
43
61
|
|
|
@@ -49,8 +67,17 @@ export function renderError(error: AxonErrorLike): string {
|
|
|
49
67
|
lines.push("", "Context:", indent(renderContext(error.context)))
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
// OUR frames are what an expected failure omits — they describe code the
|
|
71
|
+
// user did not write and cannot act on.
|
|
72
|
+
if (!error.expected) {
|
|
73
|
+
lines.push("─".repeat(RULE_WIDTH), ...error.frames.map(renderFrame))
|
|
74
|
+
}
|
|
53
75
|
|
|
76
|
+
// A cause survives either way: something deliberately attached it, and it
|
|
77
|
+
// names the underlying fault ("ECONNREFUSED" under a missing file), which
|
|
78
|
+
// is the most actionable line in the whole report. Suppressing it would
|
|
79
|
+
// leave the user reading "the file is missing" with no hint that the real
|
|
80
|
+
// problem was the network.
|
|
54
81
|
if (error.cause !== undefined) {
|
|
55
82
|
lines.push("", "Caused by:", indent(causeMessage(error.cause)))
|
|
56
83
|
}
|