happyskills 0.53.0 → 0.54.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.54.0] - 2026-05-26
11
+
12
+ ### Added
13
+
14
+ - **`intent_id` is now attached to every `/telemetry/discovery` beacon payload** (spec 260521-03 addendum § 4 — CLI half of Phase A). The CLI sources the value from the active intent envelope via a new `get_intent_id()` helper in `cli/src/utils/intent.js`, which decodes the envelope's base64url payload portion and extracts `intent_id`. Sent on all four beacon types fired from `search` and `postlex`: `rerank_started`, `rerank_completed`, `clarify_triggered`, `clarify_completed`. When no envelope is active the payload includes `intent_id: null`. Before this change, every `search.rerank` and `search.clarify` row in `analytics.events` landed with `intent_id = NULL`, defeating the addendum's goal of correlating beacons with the discovery chain.
15
+
10
16
  ## [0.53.0] - 2026-05-25
11
17
 
12
18
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happyskills",
3
- "version": "0.53.0",
3
+ "version": "0.54.0",
4
4
  "description": "Package manager for AI agent skills",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Nicolas Dao <nic@cloudlesslabs.com> (https://cloudlesslabs.com)",
@@ -15,6 +15,7 @@ const { exit_with_error, UsageError, CliError } = require('../utils/errors')
15
15
  const { EXIT_CODES } = require('../constants')
16
16
  const { slug_token_set, compute_lex_tier } = require('../utils/slug_tokens')
17
17
  const { fire_discovery_telemetry } = require('../api/telemetry')
18
+ const { get_intent_id } = require('../utils/intent')
18
19
 
19
20
  const HELP_TEXT = `Usage: happyskills postlex --query <q> --ranking <file|-> [options]
20
21
 
@@ -425,9 +426,13 @@ const run = (args) => catch_errors('Postlex failed', async () => {
425
426
  const next_step = determine_next_step(final_ordering, query, clarification_turns_used)
426
427
 
427
428
  // Fire telemetry — rerank_completed first (the rerank step succeeded);
428
- // clarify_triggered separately if applicable.
429
+ // clarify_triggered separately if applicable. `intent_id` is sourced from
430
+ // the active intent envelope so these beacons correlate with the same
431
+ // discovery chain (spec 260521-03 addendum § 4).
432
+ const intent_id = get_intent_id()
429
433
  fire_discovery_telemetry({
430
434
  event: 'rerank_completed',
435
+ intent_id,
431
436
  query,
432
437
  promoted,
433
438
  promoted_from_rank,
@@ -436,6 +441,7 @@ const run = (args) => catch_errors('Postlex failed', async () => {
436
441
  if (next_step.action === 'clarify') {
437
442
  fire_discovery_telemetry({
438
443
  event: 'clarify_triggered',
444
+ intent_id,
439
445
  query,
440
446
  reason: 'post_rerank_weak',
441
447
  turn_number: clarification_turns_used + 1,
@@ -6,6 +6,7 @@ const { exit_with_error, UsageError, AuthError } = require('../utils/errors')
6
6
  const { EXIT_CODES, VALID_SKILL_TYPES } = require('../constants')
7
7
  const { load_token } = require('../auth/token_store')
8
8
  const { fire_discovery_telemetry } = require('../api/telemetry')
9
+ const { get_intent_id } = require('../utils/intent')
9
10
 
10
11
  const HELP_TEXT = `Usage: happyskills search [query] [options]
11
12
 
@@ -221,9 +222,14 @@ const run_smart_search = (args, query, options) => catch_errors('Smart search fa
221
222
  const next_step = build_search_next_step(response, query, { with_rerank, clarification_turns_used })
222
223
 
223
224
  // Telemetry beacons (fire-and-forget). Spec § 5.1 + § 5.3.
225
+ // `intent_id` is sourced from the active intent envelope (spec 260521-03
226
+ // addendum § 4) so search.rerank / search.clarify rows correlate with
227
+ // the same discovery chain as the search.query that minted the envelope.
228
+ const intent_id = get_intent_id()
224
229
  if (with_rerank && next_step?.action === 'rank_digests_inline') {
225
230
  fire_discovery_telemetry({
226
231
  event: 'rerank_started',
232
+ intent_id,
227
233
  query,
228
234
  rerank_prompt_version: response?.rerank_prompt_version || null,
229
235
  })
@@ -231,6 +237,7 @@ const run_smart_search = (args, query, options) => catch_errors('Smart search fa
231
237
  if (with_rerank && next_step?.action === 'clarify') {
232
238
  fire_discovery_telemetry({
233
239
  event: 'clarify_triggered',
240
+ intent_id,
234
241
  query,
235
242
  reason: 'match_notice',
236
243
  turn_number: clarification_turns_used + 1,
@@ -241,6 +248,7 @@ const run_smart_search = (args, query, options) => catch_errors('Smart search fa
241
248
  // with a refined query.
242
249
  fire_discovery_telemetry({
243
250
  event: 'clarify_completed',
251
+ intent_id,
244
252
  query,
245
253
  turn_number: clarification_turns_used,
246
254
  })
@@ -39,4 +39,25 @@ const clear_envelope = () => {
39
39
  delete process.env[ENV_VAR]
40
40
  }
41
41
 
42
- module.exports = { init_from_env, init_from_flag, get_envelope, set_envelope, clear_envelope, ENV_VAR }
42
+ // Spec 260521-03 addendum § 4 Extract the intent_id UUID from the active
43
+ // envelope so the CLI can attach it to /telemetry/discovery beacon payloads.
44
+ // The envelope is `<base64url-payload>.<base64url-hmac>`; the CLI doesn't
45
+ // have the HMAC secret and doesn't need to verify — it just reads the
46
+ // payload portion. Returns null when no envelope is active or the payload
47
+ // can't be decoded.
48
+ const get_intent_id = () => {
49
+ const env = get_envelope()
50
+ if (!env) return null
51
+ try {
52
+ const dot = env.indexOf('.')
53
+ if (dot <= 0) return null
54
+ const body = env.slice(0, dot)
55
+ const json = Buffer.from(body, 'base64url').toString('utf-8')
56
+ const payload = JSON.parse(json)
57
+ return payload.intent_id || null
58
+ } catch {
59
+ return null
60
+ }
61
+ }
62
+
63
+ module.exports = { init_from_env, init_from_flag, get_envelope, set_envelope, clear_envelope, get_intent_id, ENV_VAR }