@arcforge/err 2.0.152 → 2.0.154

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcforge/err",
3
- "version": "2.0.152",
3
+ "version": "2.0.154",
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.152"
18
+ "@arcforge/types": "2.0.154"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/bun": "latest",
package/src/index.ts CHANGED
@@ -9,4 +9,4 @@ export {
9
9
  type AxonErrorSeverity,
10
10
  type AxonErrorSource,
11
11
  } from "./map"
12
- export { errScope, type AxonErrorSink } from "./sink"
12
+ export { errScope, observeErrors, type AxonErrorSink } from "./sink"
package/src/map.ts CHANGED
@@ -316,12 +316,23 @@ export const errorMap = {
316
316
  source: "manifest",
317
317
  severity: "fatal",
318
318
  },
319
- CONFIG_ENGINE_UNPARSEABLE: {
319
+ /**
320
+ * Replaced CONFIG_ENGINE_UNPARSEABLE, which described AST-editing an
321
+ * `engine: X({ ... })` call — a field that no longer exists. Warning
322
+ * rather than fatal for the deprecation window: an agent carrying
323
+ * `engine:` still boots (on the profile pool, which is what it was
324
+ * silently doing already), and the author gets told once per load what
325
+ * to write instead. It becomes fatal when the field is removed.
326
+ */
327
+ CONFIG_ENGINE_DEPRECATED: {
320
328
  code: "AX-PROJECT-033",
321
- title: "Could Not Edit The Engine In axon.config.ts",
322
- description: "The agent's `engine: X({ ... })` declaration could not be located or rewritten automatically set the model by hand.",
329
+ title: "`engine:` Is Deprecated And Ignored",
330
+ description: "This agent's axon.config.ts declares `engine:`, which the runtime no longer reads — inference is resolving against the profile's providers instead. Use `model: \"codex:gpt-5.6-terra\"` for a cortex pin, and `providers: [...]` for a source the user would not otherwise have.",
323
331
  source: "manifest",
324
- severity: "fatal",
332
+ // DEGRADED, not fatal: the agent boots and runs, but not on the
333
+ // inference its config names. That is exactly what degraded means,
334
+ // and it is the honest label for the deprecation window.
335
+ severity: "degraded",
325
336
  },
326
337
  MODULE_DEPENDENCY_INSTALL_FAILED: {
327
338
  code: "AX-PROJECT-004",
@@ -1011,7 +1022,12 @@ export const errorMap = {
1011
1022
  severity: "degraded",
1012
1023
  },
1013
1024
  KNOWLEDGE_ESCAPE: {
1014
- code: "AX-KERNEL-012",
1025
+ // Was AX-KERNEL-012, which CODEX_NOT_CONNECTED already holds and which
1026
+ // is deliberately retained retired so that code keeps its original
1027
+ // meaning. Two entries sharing a code is exactly what that comment
1028
+ // warns against — a support thread quoting AX-KERNEL-012 could have
1029
+ // meant either a disconnected provider or a path traversal.
1030
+ code: "AX-KERNEL-021",
1015
1031
  title: "Knowledge Path Escapes Store",
1016
1032
  description: "A knowledge name resolved outside the store root. Names are identifiers, not paths — traversal is refused at the boundary rather than trusted not to happen.",
1017
1033
  source: "kernel",
package/src/sink.ts CHANGED
@@ -31,7 +31,60 @@ export const errScope = {
31
31
  },
32
32
  }
33
33
 
34
- /** err()'s delivery call — the current scope's sink, or nothing (see module doc). */
34
+ /**
35
+ * Observers that see EVERY error, regardless of scope.
36
+ *
37
+ * Separate from the scoped sink above because they answer a different
38
+ * question. The scoped sink asks "whose durable record does this belong
39
+ * to" — attribution, where being wrong is a lie on disk, which is why it is
40
+ * exclusive and innermost-wins. An observer asks "did this happen at all",
41
+ * where the failure mode of getting it wrong is a missing report, not a
42
+ * false one.
43
+ *
44
+ * Making crash reporting a second scope would have meant either competing
45
+ * with the session for the same slot (whichever ran last wins, errors
46
+ * vanish) or two AsyncLocalStorage contexts to keep in sync at every entry
47
+ * point. A flat observer list has neither problem: it fires for errors
48
+ * constructed inside a session scope AND for the ones outside it, which are
49
+ * exactly the CLI and tooling failures a session log could never see.
50
+ *
51
+ * Deliberately NOT given the ability to suppress or alter the error. An
52
+ * observer is told, not consulted.
53
+ */
54
+ const observers = new Set<AxonErrorSink>()
55
+
56
+ /**
57
+ * Watch every error constructed anywhere in this process.
58
+ *
59
+ * Returns an unsubscribe function. Intended for ONE caller per process —
60
+ * the host's crash reporter — established at startup. A leaf reaching for
61
+ * this is a design error: leaves throw, hosts observe.
62
+ */
63
+ export function observeErrors(observer: AxonErrorSink): () => void {
64
+ observers.add(observer)
65
+ return () => observers.delete(observer)
66
+ }
67
+
68
+ /**
69
+ * err()'s delivery call — the current scope's sink, then every observer.
70
+ *
71
+ * An observer that throws must never break error CONSTRUCTION: err() is on
72
+ * the failure path by definition, and a reporter that turns one failure
73
+ * into two (the original plus its own) is worse than no reporter. Each is
74
+ * isolated so one bad observer cannot starve the rest.
75
+ *
76
+ * The scoped sink is called first and is deliberately NOT wrapped — it is
77
+ * the runtime's own durable record, and a failure to write it is a real
78
+ * problem the runtime should see, not something this function should hide.
79
+ */
35
80
  export function emitError(error: AxonError): void {
36
81
  storage.getStore()?.(error)
82
+
83
+ for (const observer of observers) {
84
+ try {
85
+ observer(error)
86
+ } catch {
87
+ // See above — an observer's fault never propagates into err().
88
+ }
89
+ }
37
90
  }