@namzu/sdk 28.0.0 → 30.0.0

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 (134) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +0 -1
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  6. package/dist/agents/SupervisorAgent.js +2 -4
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/config/runtime.d.ts +8 -8
  9. package/dist/contracts/a2a.d.ts +10 -10
  10. package/dist/contracts/schemas.d.ts +12 -12
  11. package/dist/manager/index.d.ts +2 -2
  12. package/dist/manager/index.d.ts.map +1 -1
  13. package/dist/manager/index.js +2 -2
  14. package/dist/manager/index.js.map +1 -1
  15. package/dist/plugin/loader.d.ts.map +1 -1
  16. package/dist/plugin/loader.js +10 -12
  17. package/dist/plugin/loader.js.map +1 -1
  18. package/dist/public-runtime.d.ts +21 -9
  19. package/dist/public-runtime.d.ts.map +1 -1
  20. package/dist/public-runtime.js +21 -9
  21. package/dist/public-runtime.js.map +1 -1
  22. package/dist/runtime/query/index.d.ts +0 -16
  23. package/dist/runtime/query/index.d.ts.map +1 -1
  24. package/dist/runtime/query/index.js +3 -10
  25. package/dist/runtime/query/index.js.map +1 -1
  26. package/dist/session/handoff/broadcast.d.ts +6 -2
  27. package/dist/session/handoff/broadcast.d.ts.map +1 -1
  28. package/dist/session/handoff/broadcast.js +1 -2
  29. package/dist/session/handoff/broadcast.js.map +1 -1
  30. package/dist/session/handoff/index.d.ts +1 -1
  31. package/dist/session/handoff/index.d.ts.map +1 -1
  32. package/dist/session/handoff/index.js +1 -1
  33. package/dist/session/handoff/index.js.map +1 -1
  34. package/dist/session/handoff/single.d.ts +7 -3
  35. package/dist/session/handoff/single.d.ts.map +1 -1
  36. package/dist/session/handoff/single.js +1 -7
  37. package/dist/session/handoff/single.js.map +1 -1
  38. package/dist/session/migration/id-prefix.d.ts +0 -11
  39. package/dist/session/migration/id-prefix.d.ts.map +1 -1
  40. package/dist/session/migration/id-prefix.js +0 -11
  41. package/dist/session/migration/id-prefix.js.map +1 -1
  42. package/dist/session/migration/index.d.ts +2 -2
  43. package/dist/session/migration/index.d.ts.map +1 -1
  44. package/dist/session/migration/index.js +2 -2
  45. package/dist/session/migration/index.js.map +1 -1
  46. package/dist/session/retention/archive.d.ts.map +1 -1
  47. package/dist/session/retention/archive.js +4 -16
  48. package/dist/session/retention/archive.js.map +1 -1
  49. package/dist/skills/loader.d.ts.map +1 -1
  50. package/dist/skills/loader.js +10 -12
  51. package/dist/skills/loader.js.map +1 -1
  52. package/dist/store/topic/memory.d.ts +4 -4
  53. package/dist/store/topic/memory.js +4 -4
  54. package/dist/tools/builtins/computer-use.d.ts +8 -8
  55. package/dist/types/agent/reactive.d.ts +0 -5
  56. package/dist/types/agent/reactive.d.ts.map +1 -1
  57. package/dist/types/agent/scheduler.d.ts +0 -10
  58. package/dist/types/agent/scheduler.d.ts.map +1 -1
  59. package/dist/types/agent/supervisor.d.ts +0 -10
  60. package/dist/types/agent/supervisor.d.ts.map +1 -1
  61. package/dist/types/authorization/index.d.ts +24 -285
  62. package/dist/types/authorization/index.d.ts.map +1 -1
  63. package/dist/types/authorization/index.js +0 -4
  64. package/dist/types/authorization/index.js.map +1 -1
  65. package/dist/types/common/index.d.ts +0 -8
  66. package/dist/types/common/index.d.ts.map +1 -1
  67. package/dist/types/common/index.js.map +1 -1
  68. package/dist/types/ids/index.d.ts +0 -15
  69. package/dist/types/ids/index.d.ts.map +1 -1
  70. package/dist/types/ids/index.js.map +1 -1
  71. package/dist/types/plugin/index.d.ts +6 -6
  72. package/dist/types/run/checkpoint-store.d.ts +0 -6
  73. package/dist/types/run/checkpoint-store.d.ts.map +1 -1
  74. package/dist/types/session/ids.d.ts +1 -3
  75. package/dist/types/session/ids.d.ts.map +1 -1
  76. package/dist/types/session/index.d.ts +1 -3
  77. package/dist/types/session/index.d.ts.map +1 -1
  78. package/dist/types/session/sub-session.d.ts +11 -22
  79. package/dist/types/session/sub-session.d.ts.map +1 -1
  80. package/dist/utils/id.d.ts +0 -8
  81. package/dist/utils/id.d.ts.map +1 -1
  82. package/dist/utils/id.js +0 -8
  83. package/dist/utils/id.js.map +1 -1
  84. package/dist/utils/log/create-logger.d.ts +3 -3
  85. package/dist/utils/log/create-logger.d.ts.map +1 -1
  86. package/dist/utils/log/create-logger.js +10 -8
  87. package/dist/utils/log/create-logger.js.map +1 -1
  88. package/dist/utils/log/process-sink.d.ts +7 -4
  89. package/dist/utils/log/process-sink.d.ts.map +1 -1
  90. package/dist/utils/log/process-sink.js +19 -13
  91. package/dist/utils/log/process-sink.js.map +1 -1
  92. package/dist/utils/log/types.d.ts +12 -7
  93. package/dist/utils/log/types.d.ts.map +1 -1
  94. package/dist/utils/log/types.js +6 -6
  95. package/dist/utils/log/types.js.map +1 -1
  96. package/dist/utils/logger.d.ts +15 -35
  97. package/dist/utils/logger.d.ts.map +1 -1
  98. package/dist/utils/logger.js +18 -136
  99. package/dist/utils/logger.js.map +1 -1
  100. package/package.json +1 -1
  101. package/src/agents/ReactiveAgent.ts +0 -1
  102. package/src/agents/SupervisorAgent.ts +2 -9
  103. package/src/manager/index.ts +0 -1
  104. package/src/plugin/loader.ts +10 -12
  105. package/src/public-runtime.ts +14 -7
  106. package/src/runtime/query/index.ts +4 -44
  107. package/src/session/handoff/broadcast.ts +7 -3
  108. package/src/session/handoff/index.ts +1 -1
  109. package/src/session/handoff/single.ts +8 -9
  110. package/src/session/migration/id-prefix.ts +0 -13
  111. package/src/session/migration/index.ts +0 -2
  112. package/src/session/retention/archive.ts +4 -17
  113. package/src/skills/loader.ts +10 -12
  114. package/src/store/topic/memory.ts +4 -4
  115. package/src/types/agent/reactive.ts +0 -5
  116. package/src/types/agent/scheduler.ts +0 -11
  117. package/src/types/agent/supervisor.ts +0 -11
  118. package/src/types/authorization/index.ts +0 -10
  119. package/src/types/common/index.ts +0 -9
  120. package/src/types/ids/index.ts +0 -15
  121. package/src/types/run/checkpoint-store.ts +0 -7
  122. package/src/types/session/ids.ts +0 -2
  123. package/src/types/session/index.ts +0 -2
  124. package/src/types/session/sub-session.ts +11 -23
  125. package/src/utils/id.ts +0 -9
  126. package/src/utils/log/create-logger.ts +10 -8
  127. package/src/utils/log/process-sink.ts +19 -13
  128. package/src/utils/log/types.ts +18 -13
  129. package/src/utils/logger.ts +18 -159
  130. package/dist/deprecated-aliases.d.ts +0 -80
  131. package/dist/deprecated-aliases.d.ts.map +0 -1
  132. package/dist/deprecated-aliases.js +0 -60
  133. package/dist/deprecated-aliases.js.map +0 -1
  134. package/src/deprecated-aliases.ts +0 -86
@@ -115,24 +115,11 @@ export type WorkspaceResolver = (
115
115
  */
116
116
  const ARCHIVABLE: ReadonlySet<SubSessionDelegationStatus> = new Set(['idle', 'failed'])
117
117
 
118
- /**
119
- * Values no code path in this package can write, kept archivable anyway.
120
- *
121
- * `merged` and `merge_rejected` sat in the one set above, and nothing has
122
- * ever produced either — so that membership described a match that could
123
- * not happen. The temptation is to delete them, and that would be wrong:
124
- * `SubSessionStore.updateSubSession` takes a whole `SubSession`, so a HOST
125
- * could have persisted one of these while the wide union permitted it, and
126
- * dropping them here would strand exactly those records as permanently
127
- * un-archivable. The union stays wide for one release precisely so such a
128
- * host can migrate; this set is the other half of that promise.
129
- *
130
- * It goes when the six deprecated members go.
131
- */
132
- const ARCHIVABLE_LEGACY: ReadonlySet<SubSessionStatus> = new Set(['merged', 'merge_rejected'])
133
-
134
118
  function isArchivable(status: SubSessionStatus): boolean {
135
- return ARCHIVABLE.has(status as SubSessionDelegationStatus) || ARCHIVABLE_LEGACY.has(status)
119
+ // No cast any more. `SubSessionStatus` narrowed to the driven variants in
120
+ // NZ-RUNREC-14, so the set and the argument are the same type and a value
121
+ // outside it cannot be constructed to ask about.
122
+ return ARCHIVABLE.has(status)
136
123
  }
137
124
 
138
125
  export interface ArchivalManagerDeps {
@@ -167,18 +167,16 @@ export async function loadSkill(
167
167
  const metadataTokens = estimateTokens(`${metadata.name}: ${metadata.description}`)
168
168
  const bodyTokens = skill.body ? estimateTokens(skill.body) : 0
169
169
 
170
- // Resolved here, not at module scope. A module-scope
171
- // `getRootLogger().child(...)` ran once, at import time — before any
172
- // host's `configureLogger()` call had a chance to run and `child()`
173
- // bakes `minLevel` into the closure `log()` reads from forever after
174
- // (`utils/logger.ts`), so whatever level was live at that one moment
175
- // was permanent. `configureLogger` replaces the `_rootLogger` binding
176
- // rather than mutating the object it points at, so caching the CHILD
177
- // (as this loader did) survives no later call at all — including the
178
- // CLI's own `configureLogger({ level: 'silent' })`.
179
- //
180
- // `log`, when the caller has one (`SkillRegistry` now does), wins over
181
- // the process default.
170
+ // Resolved here, not at module scope, and the reason outlived the defect
171
+ // it was written for. A module-scope `child(...)` of a process-wide logger
172
+ // ran once at IMPORT time and baked that moment's level in forever, which
173
+ // no later host call could undo. LOG-20 removed the process-wide logger
174
+ // altogether, so that particular trap is gone and per-call resolution
175
+ // matters more now, not less: the logger this function uses comes from its
176
+ // own `log` parameter, and anything resolved at module scope could not see
177
+ // it. Without a `log`, this loader emits nothing (`resolveLogger` is
178
+ // `NOOP_LOGGER`), which is the deliberate default rather than a fallback
179
+ // to somebody else's stream.
182
180
  const logger = resolveLogger(log).child({ [SCOPE_ATTRIBUTE]: 'skills/loader' })
183
181
  logger.debug('Loaded skill', {
184
182
  'namzu.skills.name': metadata.name,
@@ -9,10 +9,10 @@
9
9
  * cross-tenant access throws `TenantIsolationError` with no fallback.
10
10
  *
11
11
  * NZ-TOPIC-01 renamed this from `InMemoryThreadStore` (moved from
12
- * `store/thread/memory.ts`). `InMemoryThreadStore` keeps working, unaliased
13
- * — `public-runtime.ts` re-exports it as a literal identity binding to this
14
- * class, so `instanceof`/`===` both still hold for a caller who has not
15
- * migrated.
12
+ * `store/thread/memory.ts`) and re-exported the old name as an identity
13
+ * binding. NZ-TOPIC-05 removed that re-export, once 28.0.0 had carried the
14
+ * deprecation to the registry the class is reachable only as
15
+ * `InMemoryTopicStore` now.
16
16
  */
17
17
 
18
18
  import { StaleThreadError, TenantIsolationError } from '../../session/errors.js'
@@ -51,11 +51,6 @@ export interface ReactiveAgentConfig extends BaseAgentConfig {
51
51
  * canonical brick patterns hard-deny instead of executing
52
52
  * silently.
53
53
  */
54
- /**
55
- * @deprecated Renamed to `authorizationGate`. Removed in the next major.
56
- * Setting both to different configs throws.
57
- */
58
- verificationGate?: AuthorizationGateConfig
59
54
 
60
55
  authorizationGate?: AuthorizationGateConfig
61
56
 
@@ -143,14 +143,3 @@ export interface TaskScheduler {
143
143
  */
144
144
  onTaskProgress?(callback: (taskId: TaskId) => void): () => void
145
145
  }
146
-
147
- /**
148
- * @deprecated Renamed to {@link TaskScheduler}. Removed in the next major.
149
- *
150
- * "Gateway" names an object that sits at a system boundary and faces
151
- * outward — Fowler's POEAA Gateway, an API gateway, a payment gateway. This
152
- * one faces inward: it creates, waits on, continues, cancels and lists
153
- * in-process agent tasks. A reader who trusted the name expected a facade
154
- * over something external and found a scheduler.
155
- */
156
- export type TaskGateway = TaskScheduler
@@ -48,12 +48,6 @@ export interface SupervisorAgentConfig extends BaseAgentConfig {
48
48
  */
49
49
  allowDelegation?: boolean
50
50
 
51
- /**
52
- * @deprecated Renamed to {@link SupervisorAgentConfig.scheduler}. Removed
53
- * in the next major. Setting both to different instances throws.
54
- */
55
- gateway?: TaskScheduler
56
-
57
51
  scheduler?: TaskScheduler
58
52
 
59
53
  /**
@@ -167,11 +161,6 @@ export interface SupervisorAgentConfig extends BaseAgentConfig {
167
161
  * review") so the resumeHandler only fires for the truly
168
162
  * non-deterministic cases.
169
163
  */
170
- /**
171
- * @deprecated Renamed to `authorizationGate`. Removed in the next major.
172
- * Setting both to different configs throws.
173
- */
174
- verificationGate?: AuthorizationGateConfig
175
164
 
176
165
  authorizationGate?: AuthorizationGateConfig
177
166
 
@@ -187,13 +187,3 @@ export type AuthorizationGateConfig = z.infer<typeof AuthorizationGateConfigSche
187
187
  * Declared as `const` rather than `type` on purpose: a `type` alias would
188
188
  * break `typeof`, which is the only way these were ever usable.
189
189
  */
190
-
191
- /** @deprecated Renamed to {@link AuthorizationRuleSchema}. Removed in the next major. */
192
- export const VerificationRuleSchema = AuthorizationRuleSchema
193
- /** @deprecated Renamed to {@link AuthorizationGateConfigSchema}. Removed in the next major. */
194
- export const VerificationGateConfigSchema = AuthorizationGateConfigSchema
195
-
196
- /** @deprecated Renamed to {@link AuthorizationRule}. Removed in the next major. */
197
- export type VerificationRule = AuthorizationRule
198
- /** @deprecated Renamed to {@link AuthorizationGateConfig}. Removed in the next major. */
199
- export type VerificationGateConfig = AuthorizationGateConfig
@@ -15,15 +15,6 @@ export type RunExecutionStatus =
15
15
  | 'failed'
16
16
  | 'cancelled'
17
17
 
18
- /**
19
- * @deprecated Use {@link RunExecutionStatus}. Removal is a later major.
20
- *
21
- * The union is unchanged -- this is a rename, and the alias is here so
22
- * code written against the old name still compiles and warns for one
23
- * release, per SemVer's deprecate-before-remove.
24
- */
25
- export type AgentStatus = RunExecutionStatus
26
-
27
18
  export function isTerminalStatus(status: RunExecutionStatus): boolean {
28
19
  return status === 'completed' || status === 'failed' || status === 'cancelled'
29
20
  }
@@ -102,21 +102,6 @@ export type ProjectId = Id<'prj', 'ProjectId'>
102
102
  * gone (Convention #0, no silent long-lived compat).
103
103
  */
104
104
  export type TopicId = Id<'top', 'TopicId'>
105
- /**
106
- * @deprecated Use {@link TopicId}. Removal is NZ-TOPIC-05.
107
- *
108
- * NZ-TOPIC-01 introduced `TopicId` as an alias OF this name; NZ-TOPIC-04
109
- * reverses the direction. So this alias now follows `TopicId` to the `top_`
110
- * prefix instead of keeping `thd_` — a consumer still compiling against the
111
- * old name gets the new prefix, which is exactly what the major announces.
112
- *
113
- * It is kept rather than deleted because the deprecation NZ-TOPIC-01 wrote
114
- * has never reached a registry: `@namzu/sdk` on npm is 27.1.0, and the
115
- * changeset that would publish it is still unconsumed in `.changeset/`. A
116
- * removal here would be a rename with no alias wearing a major's clothes.
117
- * This release is the one that carries the warning; the next may remove it.
118
- */
119
- export type ThreadId = TopicId
120
105
  export type SubSessionId = Id<'sub', 'SubSessionId'>
121
106
  export type HandoffId = Id<'hof', 'HandoffId'>
122
107
  export type WorkspaceId = Id<'wsp', 'WorkspaceId'>
@@ -281,13 +281,6 @@ export interface LeaseSummary {
281
281
  * no reading gain — the opposite of the point.
282
282
  */
283
283
 
284
- /** @deprecated Renamed to {@link RunLease}. Removed in the next major. */
285
- export type RunClaim = RunLease
286
- /** @deprecated Renamed to {@link FencingToken}. Removed in the next major. */
287
- export type ClaimFence = FencingToken
288
- /** @deprecated Renamed to {@link LeaseSummary}. Removed in the next major. */
289
- export type ClaimSummary = LeaseSummary
290
-
291
284
  /** What a caller asks for when taking a run. */
292
285
  export interface ClaimRunOptions {
293
286
  /** Who is taking it. Recorded so an operator can see what holds a stuck run. */
@@ -8,8 +8,6 @@
8
8
 
9
9
  export type {
10
10
  ProjectId,
11
- /** @deprecated Use `TopicId`. */
12
- ThreadId,
13
11
  TopicId,
14
12
  SubSessionId,
15
13
  HandoffId,
@@ -4,8 +4,6 @@
4
4
 
5
5
  export type {
6
6
  ProjectId,
7
- /** @deprecated Use `TopicId`. */
8
- ThreadId,
9
7
  SubSessionId,
10
8
  HandoffId,
11
9
  WorkspaceId,
@@ -26,35 +26,23 @@ import type { SubSessionId, SummaryId, WorkspaceId } from './ids.js'
26
26
  export type SubSessionDelegationStatus = 'pending' | 'active' | 'idle' | 'failed' | 'archived'
27
27
 
28
28
  /**
29
- * @deprecated Use {@link SubSessionDelegationStatus}. Removal is a later
30
- * major, and the six extra members go with it.
29
+ * @deprecated Use {@link SubSessionDelegationStatus}. This is now an alias of
30
+ * it and carries no members of its own; the name goes in a later major.
31
31
  *
32
32
  * The merge half of this union was declared and never driven. Grepping
33
33
  * `packages/sdk` and `packages/cli` for each of `awaiting_merge`,
34
34
  * `pending_merge`, `merging`, `merged`, `merge_conflict` and
35
- * `merge_rejected` AS A SUB-SESSION STATUS finds no writer at all — the
36
- * many hits on `awaiting_merge` are `SessionStatus`, which is the shadowing
37
- * this type's replacement exists to end. Two of them (`merged`,
38
- * `merge_rejected`) had a READER: they sat in `ARCHIVABLE_STATUSES`, a set
39
- * that could never match on them.
35
+ * `merge_rejected` AS A SUB-SESSION STATUS found no writer at all — the many
36
+ * hits on `awaiting_merge` are `SessionStatus`, which is the shadowing this
37
+ * type's replacement exists to end. Two of them (`merged`, `merge_rejected`)
38
+ * had a READER: they sat in `ARCHIVABLE_STATUSES`, a set that could never
39
+ * match on them.
40
40
  *
41
- * The union stays this wide for one release so a host that persisted one of
42
- * these values still typechecks while it migrates.
41
+ * The union stayed wide for one release so a host that had persisted one of
42
+ * those values still typechecked while it migrated. 28.0.0 carried that
43
+ * window to the registry and NZ-RUNREC-14 closed it.
43
44
  */
44
- export type SubSessionStatus =
45
- | SubSessionDelegationStatus
46
- /** @deprecated no producer since ratification */
47
- | 'awaiting_merge'
48
- /** @deprecated no producer since ratification */
49
- | 'pending_merge'
50
- /** @deprecated no producer since ratification */
51
- | 'merging'
52
- /** @deprecated no producer since ratification */
53
- | 'merged'
54
- /** @deprecated no producer since ratification */
55
- | 'merge_conflict'
56
- /** @deprecated no producer since ratification */
57
- | 'merge_rejected'
45
+ export type SubSessionStatus = SubSessionDelegationStatus
58
46
 
59
47
  /**
60
48
  * Discriminator for how a sub-session was created. Pattern doc §4.4
package/src/utils/id.ts CHANGED
@@ -89,15 +89,6 @@ export function generateTopicId(): TopicId {
89
89
  return generateId('top_')
90
90
  }
91
91
 
92
- /**
93
- * @deprecated Use {@link generateTopicId}. Removal is NZ-TOPIC-05.
94
- *
95
- * Mints `top_` from NZ-TOPIC-04 on — an alias that kept minting `thd_`
96
- * would hand two different id spaces to one program depending on which
97
- * name the caller happened to import.
98
- */
99
- export const generateThreadId = generateTopicId
100
-
101
92
  export function generateRunId(): RunId {
102
93
  return generateId('run_')
103
94
  }
@@ -10,7 +10,8 @@
10
10
  // unwrapping, while a caller that wants to observe the pipeline reads
11
11
  // `.counters` off the same reference. That caller is now real: LOG-06's
12
12
  // `logging.pipeline` doctor check. It could not have been while every
13
- // `getRootLogger()` built a logger with its own counters — see `shared`
13
+ // the removed process-wide accessor built a logger with its own counters
14
+ // on every call — see `shared`
14
15
  // below and `utils/__tests__/log-counters-are-process-wide.test.ts`.
15
16
 
16
17
  import { getActiveSpanContext } from '../../telemetry/runtime-accessors.js'
@@ -49,9 +50,9 @@ export function newCounters(): MutableLogSinkCounters {
49
50
  /**
50
51
  * `shared` lets several loggers write through ONE counter set.
51
52
  *
52
- * Without it the counters answer a question nobody asks. `getRootLogger()`
53
- * resolves per call and builds a fresh logger each time, so every count it
54
- * accumulated died with the expression that read it -- the five fields were
53
+ * Without it the counters answer a question nobody asks. The process-wide
54
+ * accessor this seam replaced resolved per call and built a fresh logger each
55
+ * time, so every count it accumulated died with the expression that read it -- the five fields were
55
56
  * incremented on every record in the process and read by nothing, which is
56
57
  * `declared-but-undriven` with a comment above it promising `namzu doctor`
57
58
  * would read them. `installProcessSink` now owns one set for the process,
@@ -71,10 +72,11 @@ function build(
71
72
  ): CreatedLogger {
72
73
  function emit(severityText: Severity, body: string, data?: LogContext): void {
73
74
  // Read per record, off the shared mutable holder — never resolved once
74
- // and captured in this closure. Today's `Logger.child()` bakes its level
75
- // in exactly that way, which is why three module-scope loggers in the
76
- // skills/plugin loaders are frozen at `info` forever and unreachable by
77
- // any later `configureLogger` call.
75
+ // and captured in this closure. That is what lets a host retune a
76
+ // logger it has already handed out: assign `level.current` and the next
77
+ // record honours it. The alternative, resolving once at construction,
78
+ // is what froze three module-scope loggers in the skills and plugin
79
+ // loaders at `info` for the life of the process, reachable by nothing.
78
80
  if (SEVERITY_RANK[severityText] < LEVEL_RANK[options.level.current]) return
79
81
 
80
82
  const now = Date.now()
@@ -1,13 +1,16 @@
1
- // The replacement for `configureLogger`'s "one process, one global" model
2
- // still one process-wide destination (a CLI genuinely owns the whole
3
- // process), but a destination, not only a threshold: `configureLogger` could
4
- // only raise or lower a level against a fixed `process.stderr.write`.
1
+ // One process-wide DESTINATION, and nothing else process-wide.
5
2
  //
6
- // `getRootLogger` reads this: when a sink is installed, the deprecated
7
- // accessor routes through it instead of writing straight to stderr. That
8
- // bridge is what makes the seam reachable without rewriting the ~39 existing
9
- // `getRootLogger()` call sites in one commit they keep the old shape and
10
- // gain the new destination.
3
+ // This replaced a "one process, one global logger" model that could only
4
+ // raise or lower a level against a fixed `process.stderr.write` — a
5
+ // threshold, never a destination. A CLI genuinely does own its whole process,
6
+ // so a single installed sink is the right shape for that half.
7
+ //
8
+ // What it deliberately is NOT is a logger. Installing a sink does not reroute
9
+ // anything on its own: it sets where records go and owns the counter set, and
10
+ // the host builds a logger over it (`createLogger`, passing
11
+ // `getProcessSinkCounters()`) and hands that down. LOG-20 removed the global
12
+ // accessor that used to bridge the two automatically, because "automatically"
13
+ // meant a library nobody handed a logger wrote to the host's stderr.
11
14
 
12
15
  import { newCounters } from './create-logger.js'
13
16
  import type { LevelFilter, LogSink, LogSinkCounters, MutableLogSinkCounters } from './types.js'
@@ -55,10 +58,13 @@ export function getProcessSink():
55
58
  /**
56
59
  * The mutable counter set the installed destination writes through.
57
60
  *
58
- * Internal: a reader wants {@link getProcessSink}'s readonly view. This
59
- * exists so `getRootLogger`'s bridge can hand the SAME object to every
60
- * logger it builds, which is what makes the totals process-wide rather
61
- * than per-call.
61
+ * Internal in spirit, exported in fact: a mere reader wants
62
+ * {@link getProcessSink}'s readonly view. A HOST needs this one, because it
63
+ * is what makes the totals describe the process rather than one logger — it
64
+ * hands the same object to every logger it builds, so `getLogCounters()` and
65
+ * `namzu doctor`'s `logging.pipeline` check see one set of numbers. A global
66
+ * accessor used to do that on the host's behalf; with it gone, the host does
67
+ * it, which is why this is reachable from `@namzu/sdk` at all.
62
68
  */
63
69
  export function getProcessSinkCounters(): MutableLogSinkCounters | undefined {
64
70
  return _processSink?.counters
@@ -1,11 +1,11 @@
1
1
  // The LogSink seam's record and pipeline types.
2
2
  //
3
- // `../logger.ts` keeps `Logger`, `LogContext`, `getRootLogger` and
4
- // `configureLogger` completely unchanged `Logger` is in INPUT position on
5
- // the public surface (`logger?: Logger` on `RunConfig` and tool config), so
6
- // every host's existing implementation must keep satisfying it forever.
7
- // Everything here is additive: a new, structurally separate seam that does
8
- // not touch that interface.
3
+ // `../logger.ts` keeps `Logger` and `LogContext` unchanged — `Logger` is in
4
+ // INPUT position on the public surface (`logger?: Logger` on `RunConfig` and
5
+ // tool config), so every host's existing implementation must keep satisfying
6
+ // it forever. This seam was additive when it landed and stayed that way
7
+ // through LOG-20, which removed the two process-global accessors that used to
8
+ // sit beside those types without touching either type itself.
9
9
 
10
10
  /**
11
11
  * The severity vocabulary Namzu actually emits. `trace` and `fatal` are
@@ -157,13 +157,18 @@ export interface LoggerOptions {
157
157
  readonly sink: LogSink
158
158
  /**
159
159
  * A mutable holder, read per record inside `createLogger`'s dispatch —
160
- * never captured in a closure at construction. `child()` on today's
161
- * `Logger` bakes its level in exactly that way, which is why the
162
- * module-scope loggers in `skills/loader.ts`, `skills/registry.ts` and
163
- * `plugin/loader.ts` are frozen at `info` forever and unreachable by any
164
- * later `configureLogger` call verified live: `skills/loader.ts:12`,
165
- * `skills/registry.ts:10` and `plugin/loader.ts:12` all call
166
- * `getRootLogger().child({...})` at module load time.
160
+ * never captured in a closure at construction. Assigning `level.current`
161
+ * therefore retunes a logger already handed out, which is the whole reason
162
+ * this is a box and not a value.
163
+ *
164
+ * The defect it was designed against: a `child()` that baked its level in
165
+ * at construction, combined with module-scope loggers built at IMPORT
166
+ * time, froze three loaders at `info` for the life of the process and no
167
+ * later call could reach them. Those loaders resolve their logger per call
168
+ * now, and the process-wide accessor they resolved it FROM no longer
169
+ * exists — so this box is what remains of that fix, and the named
170
+ * line numbers that used to be quoted here are deliberately gone: they
171
+ * were a claim about the tree that the tree stopped honouring.
167
172
  */
168
173
  readonly level: { current: LevelFilter }
169
174
  readonly resource: Resource
@@ -1,24 +1,11 @@
1
- import { createLogger } from './log/create-logger.js'
2
- import { getProcessSink, getProcessSinkCounters } from './log/process-sink.js'
1
+ import { NOOP_LOGGER } from './log/create-logger.js'
2
+ import { getProcessSink } from './log/process-sink.js'
3
3
  import type { LogSinkCounters } from './log/types.js'
4
- import { type LevelFilter, type LogSink, SCOPE_ATTRIBUTE } from './log/types.js'
5
4
 
6
5
  export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'
7
6
 
8
7
  export type LogContext = Record<string, unknown>
9
8
 
10
- const LOG_LEVELS: Record<LogLevel, number> = {
11
- debug: 0,
12
- info: 1,
13
- warn: 2,
14
- error: 3,
15
- // `silent` sits above every emit level so the `level < minLevelNum`
16
- // guard in `log()` always short-circuits when configured. Used by
17
- // test harnesses to suppress unmocked `getRootLogger()` stderr
18
- // writes; see packages/sdk/src/test-setup.ts.
19
- silent: 4,
20
- }
21
-
22
9
  export interface Logger {
23
10
  debug(message: string, data?: LogContext): void
24
11
  info(message: string, data?: LogContext): void
@@ -27,88 +14,21 @@ export interface Logger {
27
14
  child(context: LogContext): Logger
28
15
  }
29
16
 
30
- function createLoggerImpl(name: string, minLevel: LogLevel, parentContext: LogContext): Logger {
31
- const minLevelNum = LOG_LEVELS[minLevel]
32
-
33
- function log(level: LogLevel, message: string, data?: LogContext): void {
34
- if (LOG_LEVELS[level] < minLevelNum) return
35
-
36
- const timestamp = new Date().toISOString()
37
- const prefix = `[${timestamp}] [${level.toUpperCase()}] [${name}]`
38
- const merged = { ...parentContext, ...data }
39
- const hasContext = Object.keys(merged).length > 0
40
-
41
- if (hasContext) {
42
- process.stderr.write(`${prefix} ${message} ${JSON.stringify(merged)}\n`)
43
- } else {
44
- process.stderr.write(`${prefix} ${message}\n`)
45
- }
46
- }
47
-
48
- function child(context: LogContext): Logger {
49
- const { [SCOPE_ATTRIBUTE]: scopeOverride, ...rest } = context
50
- return createLoggerImpl(typeof scopeOverride === 'string' ? scopeOverride : name, minLevel, {
51
- ...parentContext,
52
- ...rest,
53
- })
54
- }
55
-
56
- return {
57
- debug: (msg, data) => log('debug', msg, data),
58
- info: (msg, data) => log('info', msg, data),
59
- warn: (msg, data) => log('warn', msg, data),
60
- error: (msg, data) => log('error', msg, data),
61
- child,
62
- }
63
- }
64
-
65
- let _rootLogger: Logger | null = null
66
-
67
17
  /**
68
- * @deprecated Prefer `installProcessSink` (own the process's log
69
- * destination) or `createLogger` (build a logger scoped to a run, tenant or
70
- * subsystem) from `packages/sdk/src/utils/log/`. `getRootLogger` and
71
- * `configureLogger` read and write one process-wide global with no
72
- * destination lever beyond a level threshold the reason every CLI entry
73
- * point historically had only one option: switch it off entirely.
74
- * Unchanged behaviour; this JSDoc is the only edit.
75
- */
76
- export function getRootLogger(): Logger {
77
- // A process sink, when one is installed, wins over both the cached logger
78
- // and the stderr default. Resolved per CALL rather than cached, for the
79
- // same reason the new pipeline reads its level per record: a logger handed
80
- // out before `installProcessSink` ran would otherwise keep writing to
81
- // stderr forever, which is the exact shape of the three frozen loaders
82
- // this migration just fixed.
83
- //
84
- // This bridge is what lets the ~39 existing `getRootLogger()` call sites
85
- // reach a host's sink without being rewritten in one commit. They keep the
86
- // old interface and gain the new destination.
87
- const installed = getProcessSink()
88
- if (installed) return fromSink(installed.sink, installed.level)
89
-
90
- if (!_rootLogger) {
91
- _rootLogger = createLoggerImpl('namzu', 'info', {})
92
- }
93
- return _rootLogger
94
- }
95
-
96
- /**
97
- * Fall back to the process root only when nobody supplied their own. Kept
98
- * here rather than inlined at each call site so a boundary that threads a
99
- * host-supplied logger can stay entirely free of `getRootLogger()` itself.
100
- * `RunContextFactory.buildLogger` was the first caller (LOG-07); LOG-10
101
- * moved every remaining constructor across `packages/sdk/src` onto this
102
- * same seam, so this function's own fallback is now the ONLY place in the
103
- * package that reads the process-wide global outside a host's direct call
104
- * to `getRootLogger()` itself. `getRootLoggerCount` in
105
- * `scripts/log-standard.json` measures exactly that: it cannot reach zero
106
- * while an optional, non-breaking fallback exists at all — removing the
107
- * fallback (flipping the default to `NOOP_LOGGER`) is LOG-20's major, not
108
- * this seam's.
18
+ * A logger that discards, when nobody supplied one.
19
+ *
20
+ * The fallback used to be a process-wide global, so a construction with no
21
+ * logger silently wrote to stderr from a library, on a stream the host may
22
+ * be using for its own protocol. LOG-20 flipped it: a component given no
23
+ * logger produces nothing, and the drop is counted where `getLogCounters()`
24
+ * can read it.
25
+ *
26
+ * The seam stays rather than being inlined at each call site, so every
27
+ * constructor in the package expresses "logger optional" the same way and one
28
+ * line decides what optional means.
109
29
  */
110
30
  export function resolveLogger(logger: Logger | undefined): Logger {
111
- return logger ?? getRootLogger()
31
+ return logger ?? NOOP_LOGGER
112
32
  }
113
33
 
114
34
  /**
@@ -116,74 +36,13 @@ export function resolveLogger(logger: Logger | undefined): Logger {
116
36
  * when no host has claimed the process's log destination.
117
37
  *
118
38
  * `undefined` is the honest answer for that case, not a zeroed set. With no
119
- * sink installed `getRootLogger` falls back to the legacy stderr writer,
120
- * which has no pipeline and therefore no redaction pass, no size caps and
121
- * nothing to count -- reporting five zeros there would read as "nothing was
122
- * dropped, nothing was redacted", which is a stronger claim than "this was
123
- * never measured" and happens to be the claim a reader most wants to trust.
39
+ * sink installed there is no pipeline, and therefore no redaction pass, no
40
+ * size caps and nothing to count -- reporting five zeros would read as
41
+ * "nothing was dropped, nothing was redacted", a stronger claim than "this
42
+ * was never measured" and the one a reader most wants to trust.
124
43
  * `namzu doctor`'s `logging.pipeline` check turns the absence into its own
125
44
  * row rather than into a clean bill of health.
126
45
  */
127
46
  export function getLogCounters(): LogSinkCounters | undefined {
128
47
  return getProcessSink()?.counters
129
48
  }
130
-
131
- /**
132
- * Adapts the record pipeline back to the legacy `Logger` shape. `scope`
133
- * threads through recursive `child()` calls the same way `bound` does.
134
- * Previously fixed at `'namzu'` on every recursive call regardless of what
135
- * a caller bound — meaning every `getRootLogger()`-derived child logger
136
- * (the majority of call sites in this package) reported the SAME
137
- * `scope.name` no matter what `SCOPE_ATTRIBUTE` it was given. This is the
138
- * single highest-leverage line in the LOG-09 migration: see the direct
139
- * regression test in `runtime/query/__tests__/context.test.ts` and
140
- * `utils/__tests__/log-scope-attribute.test.ts`.
141
- */
142
- function fromSink(
143
- sink: LogSink,
144
- level: LevelFilter,
145
- bound: LogContext = {},
146
- scope = 'namzu',
147
- ): Logger {
148
- // The process's counter set, not a fresh one. `getRootLogger` resolves
149
- // PER CALL and lands here every time, so a logger built with its own
150
- // counters would throw the totals away between one log line and the
151
- // next -- which is what made `LogSinkCounters` unreadable by anything.
152
- const created = createLogger(
153
- {
154
- sink,
155
- level: { current: level },
156
- resource: { 'service.name': 'namzu' },
157
- scope,
158
- },
159
- getProcessSinkCounters(),
160
- )
161
- const write =
162
- (severity: 'debug' | 'info' | 'warn' | 'error') => (message: string, data?: LogContext) => {
163
- created[severity](message, { ...bound, ...data })
164
- }
165
- return {
166
- debug: write('debug'),
167
- info: write('info'),
168
- warn: write('warn'),
169
- error: write('error'),
170
- child: (context: LogContext) => {
171
- const { [SCOPE_ATTRIBUTE]: scopeOverride, ...rest } = context
172
- return fromSink(
173
- sink,
174
- level,
175
- { ...bound, ...rest },
176
- typeof scopeOverride === 'string' ? scopeOverride : scope,
177
- )
178
- },
179
- }
180
- }
181
-
182
- /**
183
- * @deprecated See `getRootLogger`'s deprecation note. `installProcessSink`
184
- * is the strictly more capable replacement — it picks a destination, not
185
- * only a threshold. Unchanged behaviour; this JSDoc is the only edit.
186
- */
187
- export function configureLogger(options: { level?: LogLevel }): void {
188
- _rootLogger = createLoggerImpl('namzu', options.level ?? 'info', {})
189
- }