@ascenda-one/gemini-hooks 0.1.27 → 0.1.28

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/README.md CHANGED
@@ -56,6 +56,19 @@ transmitted before connecting anything. Every payload carries the UTC offset
56
56
  and an idempotency key; both come from the shared sender in `tool-kit`, so
57
57
  this adapter holds only its mapping.
58
58
 
59
+ ## Privacy defaults
60
+
61
+ Metadata-only. Does not send prompts, responses, code, file paths, command
62
+ text, repository names or branch names. Correction detection runs locally on
63
+ prompt text; only the classification is transmitted.
64
+
65
+ `setup` prints the full list before it pairs — every family this adapter sends,
66
+ the refusals, and how to turn it off. That block is generated from the
67
+ adapter's own declaration in `src/setup.ts`, and a guard test
68
+ (`packages/tool-kit/tests/setupDisclosure.test.mjs`) runs the mapper and fails
69
+ the build when the declaration claims a family the mapper does not send, or
70
+ omits one it does.
71
+
59
72
  ## Event mapping
60
73
 
61
74
  See [docs/GEMINI_MAPPING.md](docs/GEMINI_MAPPING.md).
package/dist/cli.js CHANGED
@@ -407,39 +407,44 @@ var require_metricKeys = __commonJS({
407
407
  exports.METRIC_KEYS = {
408
408
  // ── Read by a backend reader ────────────────────────────────────────────
409
409
  contextWindowPeakPct: {
410
+ family: "context",
410
411
  readBy: ["backend", "handoff"],
411
412
  backendAliases: CONTEXT_WINDOW_CANONICAL_ALIASES,
412
413
  unit: "fraction of the context window (0\u20131; uncapped for >200k contexts)",
413
414
  note: "Claude Code reports a true per-session peak. Cursor reports its composer's last known occupancy under the same key \u2014 the closest its store can answer, and not the same measurement."
414
415
  },
415
416
  contextUsagePercent: {
417
+ family: "context",
416
418
  readBy: ["backend", "handoff"],
417
419
  backendAliases: CONTEXT_WINDOW_CURSOR_PERCENT_ALIASES,
418
420
  unit: "percent (0\u2013100)",
419
421
  note: "Cursor's own column name. Superseded by contextWindowPeakPct on the wire; kept because the handoff reads it and imported rows carry it. Unit-explicit on the backend: always divided by 100, never put through the fraction-or-percent heuristic."
420
422
  },
421
- promptCount: { readBy: ["backend", "handoff"], backendAliases: ["promptCount", "prompt_count"] },
422
- sessionMinutes: { readBy: ["backend"], backendAliases: ["sessionMinutes", "session_minutes"], unit: "minutes" },
423
- durationBucket: { readBy: ["backend", "handoff"], backendAliases: ["durationBucket", "duration_bucket"] },
424
- afterHoursPrompts: { readBy: ["backend", "handoff"], backendAliases: ["afterHoursPrompts", "after_hours_prompts"] },
425
- inputTokens: { readBy: ["backend"], backendAliases: ["inputTokens", "input_tokens"], unit: "tokens" },
426
- outputTokens: { readBy: ["backend"], backendAliases: ["outputTokens", "output_tokens"], unit: "tokens" },
427
- cacheReadTokens: { readBy: ["backend"], backendAliases: ["cacheReadTokens", "cache_read_tokens"], unit: "tokens" },
428
- queuedPrompts: { readBy: ["backend"], backendAliases: ["queuedPrompts", "queued_prompts"] },
429
- linesChangedBucket: { readBy: ["backend"], backendAliases: ["linesChangedBucket", "lines_changed_bucket"] },
423
+ promptCount: { family: "counts", readBy: ["backend", "handoff"], backendAliases: ["promptCount", "prompt_count"] },
424
+ sessionMinutes: { family: "session", readBy: ["backend"], backendAliases: ["sessionMinutes", "session_minutes"], unit: "minutes" },
425
+ durationBucket: { family: "session", readBy: ["backend", "handoff"], backendAliases: ["durationBucket", "duration_bucket"] },
426
+ afterHoursPrompts: { family: "clock", readBy: ["backend", "handoff"], backendAliases: ["afterHoursPrompts", "after_hours_prompts"] },
427
+ inputTokens: { family: "context", readBy: ["backend"], backendAliases: ["inputTokens", "input_tokens"], unit: "tokens" },
428
+ outputTokens: { family: "context", readBy: ["backend"], backendAliases: ["outputTokens", "output_tokens"], unit: "tokens" },
429
+ cacheReadTokens: { family: "context", readBy: ["backend"], backendAliases: ["cacheReadTokens", "cache_read_tokens"], unit: "tokens" },
430
+ queuedPrompts: { family: "counts", readBy: ["backend"], backendAliases: ["queuedPrompts", "queued_prompts"] },
431
+ linesChangedBucket: { family: "edits", readBy: ["backend"], backendAliases: ["linesChangedBucket", "lines_changed_bucket"] },
430
432
  // ── Read by the local handoff only ──────────────────────────────────────
431
- activeMinutes: { readBy: ["handoff"], unit: "minutes" },
433
+ activeMinutes: { family: "session", readBy: ["handoff"], unit: "minutes" },
432
434
  syntheticPromptLines: {
435
+ family: "reading",
433
436
  readBy: ["handoff"],
434
437
  unit: "lines",
435
438
  note: "Main-thread user lines the runtime wrote on the person's behalf (notifications, its own bookkeeping, slash-command wrappers, interrupt markers) and promptCount left out. Claude Code only; the receipt for the handoff's promptBasis. Counted per transcript, so a resumed transcript's copies count again."
436
439
  },
437
440
  dispatchedPromptLines: {
441
+ family: "reading",
438
442
  readBy: ["handoff"],
439
443
  unit: "lines",
440
444
  note: "Main-thread user lines that read as typed but are the prompt of a chip that launched the session, so promptCount left them out. Counted once per line across the store, as prompts are. Claude Code only; written beside promptBasis: typed_once."
441
445
  },
442
446
  interruptedRuns: {
447
+ family: "waiting",
443
448
  readBy: ["handoff"],
444
449
  unit: "runs",
445
450
  note: "Agent turns the person cut short with an interrupt while the turn was still going. A count per session, never a rate. Claude Code only; a handoff that counted them says so with interruptedRunsCounted, and absent means not counted, never zero."
@@ -455,11 +460,13 @@ var require_metricKeys = __commonJS({
455
460
  * they are.
456
461
  */
457
462
  handsOnMinutes: {
463
+ family: "session",
458
464
  readBy: ["handoff"],
459
465
  unit: "minutes",
460
466
  note: "Active time immediately preceding a human prompt \u2014 the only interval a transcript can show a person present for, because the prompt at its end is the evidence."
461
467
  },
462
468
  agentSupervisingMinutes: {
469
+ family: "session",
463
470
  readBy: ["handoff"],
464
471
  unit: "minutes",
465
472
  note: "The remaining active time: the agent was working and the person was not typing. NOT a claim that anyone watched it \u2014 nothing in a transcript could show that. Never render as attention."
@@ -469,127 +476,147 @@ var require_metricKeys = __commonJS({
469
476
  // a complete one, and a reader that ignores them is choosing to, rather than
470
477
  // being unable to.
471
478
  activeSplitInstants: {
479
+ family: "session",
472
480
  readBy: ["diagnostic"],
473
481
  note: "Distinct timestamps the split ran over, after collapsing ties. The denominator: two minutes off four instants and off four hundred are not the same measurement."
474
482
  },
475
483
  activeSplitUndatedLines: {
484
+ family: "session",
476
485
  readBy: ["diagnostic"],
477
486
  note: "Known lines carrying a timestamp that would not parse. Absent from the timeline, so both halves are short by an unknown amount and only this says so."
478
487
  },
479
488
  activeSplitUnposturedInstants: {
489
+ family: "posture",
480
490
  readBy: ["diagnostic"],
481
491
  note: "Instants reached before any permissionMode had been declared. Their supervising time lands in the unknown band, which is a blind spot rather than a posture."
482
492
  },
483
- afterHoursRequests: { readBy: ["handoff"] },
484
- approximateLintErrorsCount: { readBy: ["handoff"] },
485
- canceledCount: { readBy: ["handoff"] },
486
- chatEditCount: { readBy: ["handoff"] },
493
+ afterHoursRequests: { family: "clock", readBy: ["handoff"] },
494
+ approximateLintErrorsCount: { family: "outcome", readBy: ["handoff"] },
495
+ canceledCount: { family: "outcome", readBy: ["handoff"] },
496
+ chatEditCount: { family: "edits", readBy: ["handoff"] },
487
497
  compactionCount: {
498
+ family: "context",
488
499
  readBy: ["handoff"],
489
500
  note: "The backend counts context_compression_* event rows, not this key. Both are emitted; this one is for the handoff."
490
501
  },
491
502
  contextWindowPeakTokens: {
503
+ family: "context",
492
504
  readBy: ["handoff"],
493
505
  unit: "tokens",
494
506
  note: "The measured quantity, with no assumed denominator. Prefer this to the ratio for any within-person baseline."
495
507
  },
496
508
  contextWindowTokens: {
509
+ family: "context",
497
510
  readBy: ["handoff"],
498
511
  unit: "tokens",
499
512
  note: "Codex only: the model_context_window the rollout itself recorded \u2014 the real denominator its contextWindowPeakPct was computed against. Claude Code records no window and its ratio assumes 200k; absent here means the store never said."
500
513
  },
501
- date: { readBy: ["handoff"] },
502
- errorCount: { readBy: ["handoff"] },
503
- filesChangedCount: { readBy: ["handoff"] },
504
- humanChangesCount: { readBy: ["handoff"] },
505
- linesAdded: { readBy: ["handoff"] },
506
- linesRemoved: { readBy: ["handoff"] },
507
- primaryModel: { readBy: ["handoff"] },
508
- requestCount: { readBy: ["handoff"] },
509
- sessionStartedAt: { readBy: ["handoff"] },
510
- subagentComposers: { readBy: ["handoff"] },
514
+ date: { family: "clock", readBy: ["handoff"] },
515
+ errorCount: { family: "outcome", readBy: ["handoff"] },
516
+ filesChangedCount: { family: "edits", readBy: ["handoff"] },
517
+ humanChangesCount: { family: "edits", readBy: ["handoff"] },
518
+ linesAdded: { family: "edits", readBy: ["handoff"] },
519
+ linesRemoved: { family: "edits", readBy: ["handoff"] },
520
+ primaryModel: {
521
+ family: "model",
522
+ readBy: ["backend", "handoff"],
523
+ backendAliases: ["primaryModel", "primary_model"],
524
+ note: "Read twice: the desktop's local handoff folds it, and a server-side reader resolves it to group a bucket's sessions by the model that served them. Declared handoff-only until 23 Sep 2026 \u2014 see toolName for why that direction of mistake has no symptom."
525
+ },
526
+ requestCount: { family: "counts", readBy: ["handoff"] },
527
+ sessionStartedAt: { family: "session", readBy: ["handoff"] },
528
+ subagentComposers: { family: "counts", readBy: ["handoff"] },
511
529
  subagentToolCallCount: {
530
+ family: "tools",
512
531
  readBy: ["handoff"],
513
532
  note: "Claude Code only: calls made inside subagent transcripts, kept out of toolCallCount so the main-loop count matches what the wire events carry."
514
533
  },
515
- subagentTranscripts: { readBy: ["handoff"] },
534
+ subagentTranscripts: { family: "counts", readBy: ["handoff"] },
516
535
  toolCallCount: {
536
+ family: "tools",
517
537
  readBy: ["handoff"],
518
538
  note: "The backend counts ai_tool_call_started event rows, not this key \u2014 same split as compactionCount. Deduplicated on the tool-call id; see each extractor for what one call means in its store."
519
539
  },
520
540
  toolCallsUndated: {
541
+ family: "tools",
521
542
  readBy: ["handoff"],
522
543
  note: "Cursor only: calls whose every record carries an empty createdAt. Counted in toolCallCount, but no event exists for them \u2014 the wire total is smaller than this session count by exactly this number."
523
544
  },
524
- toolFailureCount: { readBy: ["handoff"] },
525
- totalEntryCount: { readBy: ["handoff"] },
545
+ toolFailureCount: { family: "outcome", readBy: ["handoff"] },
546
+ totalEntryCount: { family: "counts", readBy: ["handoff"] },
526
547
  userModifiedEditCount: {
548
+ family: "edits",
527
549
  readBy: ["handoff"],
528
550
  note: "Null, never 0 \u2014 Claude Code never sets userModified true, so 0 would assert 'no AI edit was ever corrected by hand'."
529
551
  },
530
552
  // ── Diagnostic: read by nobody, on purpose ──────────────────────────────
531
- abandonedPromptCount: { readBy: ["diagnostic"] },
553
+ abandonedPromptCount: { family: "counts", readBy: ["diagnostic"] },
532
554
  // Epoch-marker metrics. The marker is local-only and never reaches the wire
533
555
  // (see EXTRACTION_EPOCH_KIND), but it travels as a NormalizedHistoricalEvent
534
556
  // and so is keyed by the same vocabulary.
535
- windowOldest: { readBy: ["handoff"], note: "Oldest event the extraction saw \u2014 the marker's left edge." },
536
- windowNewest: { readBy: ["handoff"], note: "Newest event the extraction saw \u2014 the marker's right edge." },
537
- projectsWithNoReadableTranscript: { readBy: ["diagnostic"] },
538
- unparsedComposerHeaders: { readBy: ["diagnostic"] },
539
- unknownComposerHeaderTypes: { readBy: ["diagnostic"] },
540
- orphanedBubbles: { readBy: ["diagnostic"] },
541
- orphanedSubagentBubbles: { readBy: ["diagnostic"] },
542
- sessionsWithoutTimeline: { readBy: ["diagnostic"] },
557
+ windowOldest: { family: "session", readBy: ["handoff"], note: "Oldest event the extraction saw \u2014 the marker's left edge." },
558
+ windowNewest: { family: "session", readBy: ["handoff"], note: "Newest event the extraction saw \u2014 the marker's right edge." },
559
+ projectsWithNoReadableTranscript: { family: "reading", readBy: ["diagnostic"] },
560
+ unparsedComposerHeaders: { family: "reading", readBy: ["diagnostic"] },
561
+ unknownComposerHeaderTypes: { family: "reading", readBy: ["diagnostic"] },
562
+ orphanedBubbles: { family: "reading", readBy: ["diagnostic"] },
563
+ orphanedSubagentBubbles: { family: "reading", readBy: ["diagnostic"] },
564
+ sessionsWithoutTimeline: { family: "reading", readBy: ["diagnostic"] },
543
565
  sessionsWithOnlyInheritedLines: {
566
+ family: "reading",
544
567
  readBy: ["diagnostic"],
545
568
  note: "Claude Code transcripts holding nothing but a copy of an ancestor's history, so no line's instant is theirs and no session is emitted for them. Excluded by rule, not a read failure: the work is in the transcript that owns it."
546
569
  },
547
- emptyComposers: { readBy: ["diagnostic"] },
548
- apiErrorCount: { readBy: ["diagnostic"] },
549
- assistantTurns: { readBy: ["diagnostic"] },
550
- compactionAutoCount: { readBy: ["diagnostic"] },
551
- compactionManualCount: { readBy: ["diagnostic"] },
552
- editDayCount: { readBy: ["diagnostic"] },
553
- emptyChatSessions: { readBy: ["diagnostic"] },
554
- gitBranch: { readBy: ["diagnostic"] },
555
- linesChanged: { readBy: ["diagnostic"] },
556
- malformedChatSessionLines: { readBy: ["diagnostic"] },
557
- malformedHistoryEntries: { readBy: ["diagnostic"] },
558
- mode: { readBy: ["diagnostic"] },
559
- modelCount: { readBy: ["diagnostic"] },
560
- modelSwitchCount: { readBy: ["diagnostic"] },
561
- rapidRepromptCount: { readBy: ["diagnostic"] },
562
- schemaUnreadable: { readBy: ["diagnostic"] },
563
- sessionCount: { readBy: ["diagnostic"] },
564
- sessionsFromBubbleTimeline: { readBy: ["diagnostic"] },
565
- sessionsFromCheckpointTimeline: { readBy: ["diagnostic"] },
566
- sessionsFromRecencyTimeline: { readBy: ["diagnostic"] },
567
- subagentAssistantTurns: { readBy: ["diagnostic"] },
568
- subagentPrompts: { readBy: ["diagnostic"] },
569
- subagentTokensTotal: { readBy: ["diagnostic"] },
570
+ emptyComposers: { family: "reading", readBy: ["diagnostic"] },
571
+ apiErrorCount: { family: "outcome", readBy: ["diagnostic"] },
572
+ assistantTurns: { family: "counts", readBy: ["diagnostic"] },
573
+ compactionAutoCount: { family: "context", readBy: ["diagnostic"] },
574
+ compactionManualCount: { family: "context", readBy: ["diagnostic"] },
575
+ editDayCount: { family: "clock", readBy: ["diagnostic"] },
576
+ emptyChatSessions: { family: "reading", readBy: ["diagnostic"] },
577
+ gitBranch: { family: "repo", readBy: ["diagnostic"] },
578
+ linesChanged: { family: "edits", readBy: ["diagnostic"] },
579
+ malformedChatSessionLines: { family: "reading", readBy: ["diagnostic"] },
580
+ malformedHistoryEntries: { family: "reading", readBy: ["diagnostic"] },
581
+ mode: { family: "posture", readBy: ["diagnostic"] },
582
+ modelCount: { family: "model", readBy: ["diagnostic"] },
583
+ modelSwitchCount: { family: "model", readBy: ["diagnostic"] },
584
+ rapidRepromptCount: { family: "counts", readBy: ["diagnostic"] },
585
+ schemaUnreadable: { family: "reading", readBy: ["diagnostic"] },
586
+ sessionCount: { family: "session", readBy: ["diagnostic"] },
587
+ sessionsFromBubbleTimeline: { family: "reading", readBy: ["diagnostic"] },
588
+ sessionsFromCheckpointTimeline: { family: "reading", readBy: ["diagnostic"] },
589
+ sessionsFromRecencyTimeline: { family: "reading", readBy: ["diagnostic"] },
590
+ subagentAssistantTurns: { family: "counts", readBy: ["diagnostic"] },
591
+ subagentPrompts: { family: "counts", readBy: ["diagnostic"] },
592
+ subagentTokensTotal: { family: "context", readBy: ["diagnostic"] },
570
593
  toolName: {
571
- readBy: ["diagnostic"],
572
- note: "Set per ai_tool_call_started event by #43's extractors and shipped in wire metadata, but no reader resolves it server-side yet (the backend's ToolName column is MCP audit, not telemetry). Registered after the fact: #38 and #43 merged past each other, and the union caught it on the next compile \u2014 metaLines all over again."
594
+ family: "tools",
595
+ readBy: ["backend"],
596
+ backendAliases: ["toolName", "tool_name"],
597
+ note: "Set per ai_tool_call_started event by #43's extractors. Declared diagnostic until 23 Sep 2026 on the belief that nothing resolved it server-side; two backend readers do \u2014 one to see a sub-agent dispatch, one composing per-tool metrics. This is the inverse of the mismatch this module was written for: contextUsagePercent was a key nobody read, and a key a reader resolves without being declared drops nothing, so it has no symptom at all. What it costs is that readBy stops being a true census of what the server sees, which is what you consult when deciding what a collector should stop sending. Registered after the fact in the first place: #38 and #43 merged past each other, and the union caught it on the next compile \u2014 metaLines all over again."
573
598
  },
574
- toolResultCount: { readBy: ["diagnostic"] },
575
- toolResultErrorCount: { readBy: ["diagnostic"] },
576
- unknownBubbles: { readBy: ["diagnostic"] },
577
- unknownLines: { readBy: ["diagnostic"] },
599
+ toolResultCount: { family: "tools", readBy: ["diagnostic"] },
600
+ toolResultErrorCount: { family: "outcome", readBy: ["diagnostic"] },
601
+ unknownBubbles: { family: "reading", readBy: ["diagnostic"] },
602
+ unknownLines: { family: "reading", readBy: ["diagnostic"] },
578
603
  metaLines: {
604
+ family: "reading",
579
605
  readBy: ["diagnostic"],
580
606
  note: 'Recognised-but-skipped transcript machinery (file-history-snapshot, queued-command, \u2026). Split out of unknownLines by #43 so that number keeps meaning "a type nobody has looked at". Registered here after the fact: #41 and #43 merged past each other, and the union caught it on the next compile \u2014 which is this module doing its job.'
581
607
  },
582
- unparsedBubbles: { readBy: ["diagnostic"] },
583
- unparsedChatSessionFiles: { readBy: ["diagnostic"] },
584
- unparsedHistoryFiles: { readBy: ["diagnostic"] },
585
- unparsedLines: { readBy: ["diagnostic"] },
586
- unreadableChatSessionFiles: { readBy: ["diagnostic"] },
587
- unreadableHistoryFiles: { readBy: ["diagnostic"] },
608
+ unparsedBubbles: { family: "reading", readBy: ["diagnostic"] },
609
+ unparsedChatSessionFiles: { family: "reading", readBy: ["diagnostic"] },
610
+ unparsedHistoryFiles: { family: "reading", readBy: ["diagnostic"] },
611
+ unparsedLines: { family: "reading", readBy: ["diagnostic"] },
612
+ unreadableChatSessionFiles: { family: "reading", readBy: ["diagnostic"] },
613
+ unreadableHistoryFiles: { family: "reading", readBy: ["diagnostic"] },
588
614
  unreadableRolloutFiles: {
615
+ family: "reading",
589
616
  readBy: ["diagnostic"],
590
617
  note: "Codex only: rollout files the extractor meant to read and could not open. Summed into the import's read-failure warning, so a store that is partly unreadable says so rather than reporting a short window as the whole."
591
618
  },
592
- unrecognisedChatSessionFiles: { readBy: ["diagnostic"] }
619
+ unrecognisedChatSessionFiles: { family: "reading", readBy: ["diagnostic"] }
593
620
  };
594
621
  function backendMetricKeys() {
595
622
  return Object.entries(exports.METRIC_KEYS).filter(([, spec]) => spec.readBy.includes("backend")).map(([key, spec]) => [key, spec.backendAliases ?? [key]]);
@@ -602,7 +629,7 @@ var require_out = __commonJS({
602
629
  "../packages/tool-contract/out/index.js"(exports) {
603
630
  "use strict";
604
631
  Object.defineProperty(exports, "__esModule", { value: true });
605
- exports.backendMetricKeys = exports.METRIC_KEYS = exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.TOOL_EVENT_DELIVERED_STATUSES = exports.IDEMPOTENCY_KEY_MAX_LENGTH = exports.EVENT_METADATA_FIELDS = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = exports.ASCENDA_ELAPSED_QUANTITIES = exports.ASCENDA_ACTIVE_TIME_QUANTITIES = exports.ASCENDA_TOOL_TYPES = exports.ASCENDA_TELEMETRY_SOURCES = void 0;
632
+ exports.backendMetricKeys = exports.METRIC_KEYS = exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.TOOL_EVENT_DELIVERED_STATUSES = exports.IDEMPOTENCY_KEY_MAX_LENGTH = exports.EVENT_METADATA_FIELDS = exports.EVENT_METADATA_DISCLOSURE = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = exports.ASCENDA_ELAPSED_QUANTITIES = exports.ASCENDA_ACTIVE_TIME_QUANTITIES = exports.ASCENDA_TOOL_TYPES = exports.ASCENDA_TELEMETRY_SOURCES = void 0;
606
633
  exports.combineDisjointHalves = combineDisjointHalves;
607
634
  exports.requireComparableQuantities = requireComparableQuantities;
608
635
  exports.ASCENDA_TELEMETRY_SOURCES = [
@@ -670,6 +697,51 @@ var require_out = __commonJS({
670
697
  "review_given",
671
698
  "pull_request_opened"
672
699
  ];
700
+ exports.EVENT_METADATA_DISCLOSURE = {
701
+ language: "edits",
702
+ fileType: "edits",
703
+ durationBucket: "session",
704
+ tokenPressureBucket: "context",
705
+ linesChangedBucket: "edits",
706
+ commandClass: "counts",
707
+ gitAction: "git",
708
+ milestoneKind: "git",
709
+ branchHash: "repo",
710
+ autonomyMode: "posture",
711
+ modelClass: "model",
712
+ modelId: "model",
713
+ userModified: "edits",
714
+ outcome: "outcome",
715
+ trigger: "outcome",
716
+ promptClass: "counts",
717
+ reason: "outcome",
718
+ afterHours: "clock",
719
+ activity: "counts",
720
+ // One constant word, from the agent itself. The session sentence names it.
721
+ sessionEndReason: "session",
722
+ interruptionKind: "waiting",
723
+ // Free text by type, and written by no hook mapper — the IDE extension's
724
+ // test signal is the only producer. The setup disclosure's third refusal
725
+ // line says as much, and tool-kit's guard test fails if a mapper starts.
726
+ message: "counts",
727
+ host: "session",
728
+ // One constant word from the host's own environment: this machine, or a
729
+ // hosted session. The session sentence names it.
730
+ runtime: "session",
731
+ toolName: "tools",
732
+ simulated: "transport",
733
+ relatedEventType: "transport",
734
+ skillVersion: "transport",
735
+ // A hash computed before it reaches any collector; the task itself is never
736
+ // seen by this code, so there is nothing about the work here to disclose.
737
+ taskFingerprint: "transport",
738
+ importKey: "transport",
739
+ extractionId: "transport",
740
+ importSchema: "transport",
741
+ // The version of the Ascenda collector that sent this event. Names the
742
+ // build, and nothing about the work it observed.
743
+ collectorVersion: "transport"
744
+ };
673
745
  exports.EVENT_METADATA_FIELDS = [
674
746
  "language",
675
747
  "fileType",
@@ -693,13 +765,17 @@ var require_out = __commonJS({
693
765
  "message",
694
766
  "host",
695
767
  "toolName",
768
+ "runtime",
769
+ "sessionEndReason",
770
+ "interruptionKind",
696
771
  "simulated",
697
772
  "relatedEventType",
698
773
  "skillVersion",
699
774
  "taskFingerprint",
700
775
  "importKey",
701
776
  "extractionId",
702
- "importSchema"
777
+ "importSchema",
778
+ "collectorVersion"
703
779
  ];
704
780
  exports.IDEMPOTENCY_KEY_MAX_LENGTH = 128;
705
781
  exports.TOOL_EVENT_DELIVERED_STATUSES = ["accepted", "duplicate"];
@@ -865,6 +941,21 @@ var require_payload = __commonJS({
865
941
  }
866
942
  });
867
943
 
944
+ // ../packages/tool-kit/out/collectorVersion.js
945
+ var require_collectorVersion = __commonJS({
946
+ "../packages/tool-kit/out/collectorVersion.js"(exports) {
947
+ "use strict";
948
+ Object.defineProperty(exports, "__esModule", { value: true });
949
+ exports.COLLECTOR_VERSION = exports.UNRELEASED_COLLECTOR_VERSION = void 0;
950
+ exports.describeCollectorVersion = describeCollectorVersion;
951
+ exports.UNRELEASED_COLLECTOR_VERSION = "unreleased";
952
+ exports.COLLECTOR_VERSION = "0.1.28" ? "0.1.28" : exports.UNRELEASED_COLLECTOR_VERSION;
953
+ function describeCollectorVersion(version = exports.COLLECTOR_VERSION) {
954
+ return version === exports.UNRELEASED_COLLECTOR_VERSION ? `${version} (built from a checkout, not a release)` : version;
955
+ }
956
+ }
957
+ });
958
+
868
959
  // ../packages/tool-kit/out/eventLog.js
869
960
  var require_eventLog = __commonJS({
870
961
  "../packages/tool-kit/out/eventLog.js"(exports) {
@@ -1618,6 +1709,7 @@ var require_eventSender = __commonJS({
1618
1709
  var tokenStore_1 = require_tokenStore();
1619
1710
  var stateStore_1 = require_stateStore();
1620
1711
  var payload_1 = require_payload();
1712
+ var collectorVersion_1 = require_collectorVersion();
1621
1713
  var AscendaSemanticEventError = class extends Error {
1622
1714
  constructor(message) {
1623
1715
  super(message);
@@ -1639,9 +1731,12 @@ var require_eventSender = __commonJS({
1639
1731
  provenance: tool_contract_1.ASCENDA_PROVENANCE,
1640
1732
  privacyMode: "metadata_only",
1641
1733
  ...mapped,
1642
- metadata: mapped.metadata ?? {}
1734
+ metadata: withCollectorVersion(mapped.metadata)
1643
1735
  };
1644
1736
  }
1737
+ function withCollectorVersion(metadata) {
1738
+ return { ...metadata, collectorVersion: collectorVersion_1.COLLECTOR_VERSION };
1739
+ }
1645
1740
  var DEFAULT_TIMEOUT_MS = 5e3;
1646
1741
  var RETRY_DELAY_MS = 250;
1647
1742
  var AscendaEventSender = class {
@@ -1661,6 +1756,30 @@ var require_eventSender = __commonJS({
1661
1756
  async send(mapped) {
1662
1757
  return this.post(buildEventPayload(this.config, mapped));
1663
1758
  }
1759
+ /**
1760
+ * Writes the event to the outbox and returns without touching the network.
1761
+ * The next outbox pass delivers it, from whichever hook of this
1762
+ * installation sends next.
1763
+ *
1764
+ * For a hook whose host is exiting. A process the host may kill at any
1765
+ * moment can't wait on a send, and one killed mid-send dies before
1766
+ * {@link post} gets to queue the payload, so the event goes with it. Here
1767
+ * the payload is on disk before anything can go wrong.
1768
+ *
1769
+ * Returns whether it is. When the write fails the journal says the event
1770
+ * was lost; nothing else could have kept it.
1771
+ */
1772
+ queue(mapped) {
1773
+ const payload = buildEventPayload(this.config, mapped);
1774
+ const queued = this.enqueue(payload);
1775
+ if (!queued) {
1776
+ this.lastState = (0, stateStore_1.recordSendOutcome)(this.stateFilePath(), this.config.toolInstallationId, "other", {
1777
+ detail: `${payload.eventType}: outbox write failed, event lost`
1778
+ });
1779
+ }
1780
+ this.log(payload, "not_sent", queued ? "queued" : void 0);
1781
+ return queued;
1782
+ }
1664
1783
  /**
1665
1784
  * Sends one of the six agent-observed types (dark-flow-gap-analysis §2.1).
1666
1785
  * Distinct from {@link send} rather than an option on it, because the
@@ -1701,7 +1820,7 @@ var require_eventSender = __commonJS({
1701
1820
  consentScope: tool_contract_1.ASCENDA_SEMANTIC_CONSENT_SCOPE,
1702
1821
  provenance: tool_contract_1.ASCENDA_SEMANTIC_PROVENANCE,
1703
1822
  privacyMode: "metadata_only",
1704
- metadata: mapped.metadata
1823
+ metadata: withCollectorVersion(mapped.metadata)
1705
1824
  };
1706
1825
  return this.post(payload);
1707
1826
  }
@@ -1732,7 +1851,7 @@ var require_eventSender = __commonJS({
1732
1851
  consentScope: tool_contract_1.ASCENDA_COLLABORATION_CONSENT_SCOPE,
1733
1852
  provenance: tool_contract_1.ASCENDA_PROVENANCE,
1734
1853
  privacyMode: "metadata_only",
1735
- metadata: mapped.metadata ?? {}
1854
+ metadata: withCollectorVersion(mapped.metadata)
1736
1855
  };
1737
1856
  return this.post(payload);
1738
1857
  }
@@ -1957,6 +2076,8 @@ var require_eventSender = __commonJS({
1957
2076
  }
1958
2077
  /** Never throws: a renewal that errors is a failed renewal, not a failed turn. */
1959
2078
  async renewEventToken() {
2079
+ if (this.config.renewToken === false)
2080
+ return false;
1960
2081
  try {
1961
2082
  const renewed = await (0, http_1.renewToolToken)(this.config.apiBaseUrl, this.eventWriteToken, this.signal());
1962
2083
  if (!renewed)
@@ -2169,6 +2290,7 @@ var require_credentials = __commonJS({
2169
2290
  exports.readMachineCredentials = readMachineCredentials;
2170
2291
  exports.writeMachineCredentials = writeMachineCredentials;
2171
2292
  exports.writeTopLevelCredentials = writeTopLevelCredentials;
2293
+ exports.isLocalOnlyHostInstall = isLocalOnlyHostInstall;
2172
2294
  exports.readHostCredentials = readHostCredentials;
2173
2295
  exports.writeHostCredentials = writeHostCredentials;
2174
2296
  exports.removeHostCredentials = removeHostCredentials;
@@ -2205,6 +2327,14 @@ var require_credentials = __commonJS({
2205
2327
  const existing = readMachineCredentials();
2206
2328
  writeMachineCredentials({ ...credentials, ...existing?.tools ? { tools: existing.tools } : {} });
2207
2329
  }
2330
+ function isLocalOnlyHostInstall(host, hasToken) {
2331
+ const credentials = readHostCredentials(host);
2332
+ if (!credentials?.localOnly)
2333
+ return false;
2334
+ if (process.env.ASCENDA_EVENT_WRITE_TOKEN)
2335
+ return false;
2336
+ return !(credentials.toolInstallationId && hasToken(credentials.toolInstallationId));
2337
+ }
2208
2338
  function readHostCredentials(host) {
2209
2339
  const entry = readMachineCredentials()?.tools?.[host];
2210
2340
  if (!entry || typeof entry !== "object" || Array.isArray(entry))
@@ -2773,6 +2903,9 @@ var require_hookAdapter = __commonJS({
2773
2903
  if (events.length === 0)
2774
2904
  return;
2775
2905
  const notice = options.onNotice ?? ((message) => console.error(message));
2906
+ const hasToken = (id) => (0, tokenStore_1.readTokenFile)(process.env.ASCENDA_EVENT_WRITE_TOKEN_FILE ?? (0, tokenStore_1.defaultTokenFilePath)(id)) !== void 0;
2907
+ if (options.host && (0, credentials_1.isLocalOnlyHostInstall)(options.host, hasToken))
2908
+ return;
2776
2909
  let config;
2777
2910
  try {
2778
2911
  config = loadCliAgentConfig(options.toolType, options.sessionId, options.cwd, options);
@@ -2838,6 +2971,51 @@ var require_hookAdapter = __commonJS({
2838
2971
  }
2839
2972
  });
2840
2973
 
2974
+ // ../packages/tool-kit/out/setupDisclosure.js
2975
+ var require_setupDisclosure = __commonJS({
2976
+ "../packages/tool-kit/out/setupDisclosure.js"(exports) {
2977
+ "use strict";
2978
+ Object.defineProperty(exports, "__esModule", { value: true });
2979
+ exports.FREE_TEXT_KEYS = exports.REFUSALS = exports.ALWAYS_SENT = exports.FAMILY_SENTENCES = void 0;
2980
+ exports.renderSetupDisclosure = renderSetupDisclosure;
2981
+ exports.FAMILY_SENTENCES = {
2982
+ session: "when a session starts and ends, the one-word reason for the end if your agent gives one, how long each turn took, and whether it ran on this machine or in a hosted cloud session",
2983
+ counts: "how many prompts you sent, how many replies came back, and what class of command ran in a terminal \u2014 test, lint, build, git and so on",
2984
+ tools: "the name of each tool your agent calls",
2985
+ outcome: "whether a call succeeded, failed, or was interrupted",
2986
+ clock: "your machine's offset from UTC, so an evening is read as an evening",
2987
+ repo: "your project and branch as digests computed on this machine \u2014 never the names themselves",
2988
+ model: "which model served the session, by name and by tier",
2989
+ posture: "the permission posture your agent was working under \u2014 asking each time, accepting edits, and so on",
2990
+ git: "that a commit, push or revert happened, and when a pull request opened or merged",
2991
+ edits: "roughly how much a file changed, as a bucket, and whether you edited it after the agent wrote it",
2992
+ context: "how full the context window got",
2993
+ waiting: "that your agent stopped and waited for you, as one word: permission_request, idle_prompt, or other",
2994
+ reading: "what the reader could not make sense of \u2014 lines it failed to parse, files it could not open, and prompts it judged machine-written rather than typed"
2995
+ };
2996
+ exports.ALWAYS_SENT = ["session", "counts", "tools", "outcome", "repo", "clock"];
2997
+ exports.REFUSALS = [
2998
+ "Never your prompts, your agent's replies, or anything inside a file.",
2999
+ "Never a file path, a command line, or a repository or branch name.",
3000
+ "Content is read here only to choose one of a fixed set of labels, then discarded. No mapper writes it to an event, and a test fails the build if one starts."
3001
+ ];
3002
+ exports.FREE_TEXT_KEYS = ["message"];
3003
+ function renderSetupDisclosure({ sends, displayName }) {
3004
+ const declared = /* @__PURE__ */ new Set([...exports.ALWAYS_SENT, ...sends]);
3005
+ const ordered = Object.keys(exports.FAMILY_SENTENCES).filter((family) => declared.has(family));
3006
+ const lines = [
3007
+ `What this sends from ${displayName}, once paired:`,
3008
+ ...ordered.map((family) => ` \xB7 ${exports.FAMILY_SENTENCES[family]}`),
3009
+ "",
3010
+ ...exports.REFUSALS.map((line) => ` ${line}`),
3011
+ "",
3012
+ " Turn it off by revoking this tool in the Ascenda app."
3013
+ ];
3014
+ return lines.join("\n");
3015
+ }
3016
+ }
3017
+ });
3018
+
2841
3019
  // ../packages/tool-kit/out/cliAgentSetup.js
2842
3020
  var require_cliAgentSetup = __commonJS({
2843
3021
  "../packages/tool-kit/out/cliAgentSetup.js"(exports) {
@@ -2889,9 +3067,11 @@ var require_cliAgentSetup = __commonJS({
2889
3067
  var fs = __importStar(__require("fs"));
2890
3068
  var os2 = __importStar(__require("os"));
2891
3069
  var path2 = __importStar(__require("path"));
3070
+ var collectorVersion_1 = require_collectorVersion();
2892
3071
  var credentials_1 = require_credentials();
2893
3072
  var hookAdapter_1 = require_hookAdapter();
2894
3073
  var http_1 = require_http();
3074
+ var setupDisclosure_1 = require_setupDisclosure();
2895
3075
  var tokenStore_1 = require_tokenStore();
2896
3076
  var MANAGEMENT_COMMANDS = /* @__PURE__ */ new Set(["setup", "install", "status", "uninstall", "-h", "--help"]);
2897
3077
  function isCliAgentManagementCommand2(argument) {
@@ -2912,6 +3092,7 @@ Options
2912
3092
  --local [port] shorthand for the local dev server (default port 4477)
2913
3093
  --tool-installation-id <id> reuse an existing pairing instead of creating one
2914
3094
  --token <eventWriteToken> reuse an existing token (stored 0600, never printed)
3095
+ --no-pair install without pairing: local features on, nothing sent
2915
3096
  --scope project|user where hooks are registered (default project)
2916
3097
  --project-dir <path> project root for --scope project (default cwd)
2917
3098
  --dry-run print what would change, write nothing
@@ -2936,14 +3117,35 @@ Options
2936
3117
  return uninstall(options, spec);
2937
3118
  const apiBaseUrl = (options.apiBaseUrl ?? (0, credentials_1.readHostCredentials)(spec.host)?.apiBaseUrl ?? hookAdapter_1.DEFAULT_API_BASE_URL).replace(/\/$/, "");
2938
3119
  console.log(`Ascenda setup for ${spec.displayName} \u2014 ${apiBaseUrl}`);
2939
- const identity = await resolveIdentity(apiBaseUrl, options, spec);
2940
- if (!identity)
2941
- return 1;
2942
- console.log(` pairing ${identity.toolInstallationId}${identity.paired ? " (new)" : " (existing)"}`);
3120
+ console.log(`
3121
+ ${(0, setupDisclosure_1.renderSetupDisclosure)({ sends: spec.sends, displayName: spec.displayName })}
3122
+ `);
3123
+ let identity;
3124
+ let unpairedReason;
3125
+ if (options.skipPairing) {
3126
+ unpairedReason = "asked not to pair (--no-pair)";
3127
+ } else {
3128
+ identity = await resolveIdentity(apiBaseUrl, options, spec);
3129
+ if (identity) {
3130
+ console.log(` pairing ${identity.toolInstallationId}${identity.paired ? " (new)" : " (existing)"}`);
3131
+ } else {
3132
+ unpairedReason = "pairing did not finish \u2014 installing the local half anyway";
3133
+ }
3134
+ }
3135
+ if (unpairedReason) {
3136
+ console.log(` pairing none: ${unpairedReason}`);
3137
+ console.log(" local features on, nothing is sent");
3138
+ }
2943
3139
  const binary = installBinary(spec, options.dryRun);
2944
3140
  console.log(` hook binary ${binary}`);
2945
3141
  if (!options.dryRun) {
2946
- (0, credentials_1.writeHostCredentials)(spec.host, { apiBaseUrl, toolInstallationId: identity.toolInstallationId, pairedAt: (/* @__PURE__ */ new Date()).toISOString() });
3142
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3143
+ const toolInstallationId = identity?.toolInstallationId ?? options.toolInstallationId ?? (0, credentials_1.readHostCredentials)(spec.host)?.toolInstallationId ?? `${spec.toolType}:${crypto.randomUUID()}`;
3144
+ (0, credentials_1.writeHostCredentials)(spec.host, {
3145
+ apiBaseUrl,
3146
+ toolInstallationId,
3147
+ ...identity ? { pairedAt: now } : { localOnly: true, installedAt: now }
3148
+ });
2947
3149
  }
2948
3150
  console.log(` credentials ${(0, credentials_1.credentialsFilePath)()} (tools.${spec.host})`);
2949
3151
  const settingsFile = spec.settings.settingsPath(options.scope, options.projectDir);
@@ -2957,6 +3159,9 @@ Options
2957
3159
  }
2958
3160
  console.log(`
2959
3161
  Done. ${spec.restartHint}`);
3162
+ if (!identity) {
3163
+ console.log(`Pair later: npx ${spec.packageName} setup`);
3164
+ }
2960
3165
  console.log(`Check anytime: npx ${spec.packageName} status`);
2961
3166
  return 0;
2962
3167
  }
@@ -2965,6 +3170,7 @@ Done. ${spec.restartHint}`);
2965
3170
  scope: "project",
2966
3171
  projectDir: process.cwd(),
2967
3172
  dryRun: false,
3173
+ skipPairing: false,
2968
3174
  action: "install"
2969
3175
  };
2970
3176
  for (let i = 0; i < argv.length; i++) {
@@ -3014,6 +3220,14 @@ Done. ${spec.restartHint}`);
3014
3220
  case "--dry-run":
3015
3221
  options.dryRun = true;
3016
3222
  break;
3223
+ case "--no-pair":
3224
+ // `--no-pairing` is the spelling this landed under first. Both appeared
3225
+ // only in an unreleased changelog, so this alias exists to spare anyone
3226
+ // reading that draft, and `--no-pair` — the Claude Code adapter's own
3227
+ // spelling — is the documented one.
3228
+ case "--no-pairing":
3229
+ options.skipPairing = true;
3230
+ break;
3017
3231
  case "-h":
3018
3232
  case "--help":
3019
3233
  options.action = "help";
@@ -3165,9 +3379,11 @@ ${serialised}`);
3165
3379
  const settings = readSettings(settingsFile);
3166
3380
  const registered = spec.hookEvents.filter((event) => (settings.hooks?.[event] ?? []).some((entry) => isOurs(entry, spec))).length;
3167
3381
  const stale = findStaleHookCommands(settings, binary, spec);
3382
+ const localOnly = (0, credentials_1.isLocalOnlyHostInstall)(spec.host, (id) => (0, tokenStore_1.readTokenFile)((0, tokenStore_1.defaultTokenFilePath)(id)) !== void 0);
3383
+ console.log(`version ${(0, collectorVersion_1.describeCollectorVersion)()}`);
3168
3384
  console.log(`api base url ${credentials?.apiBaseUrl ?? "\u2014 not configured"}`);
3169
- console.log(`pairing ${credentials?.toolInstallationId ?? "\u2014 not paired"}`);
3170
- console.log(`token ${tokenFile && (0, tokenStore_1.readTokenFile)(tokenFile) ? "present" : "\u2014 missing"}`);
3385
+ console.log(`pairing ${credentials?.toolInstallationId ?? "\u2014 not paired"}${localOnly ? " (installed, not paired \u2014 local features active, telemetry inactive)" : ""}`);
3386
+ console.log(`token ${localOnly ? "\u2014 none needed until this install is paired" : tokenFile && (0, tokenStore_1.readTokenFile)(tokenFile) ? "present" : "\u2014 missing"}`);
3171
3387
  console.log(`hook binary ${fs.existsSync(binary) ? binary : "\u2014 not installed"}`);
3172
3388
  console.log(`hooks ${registered}/${spec.hookEvents.length} registered in ${settingsFile}`);
3173
3389
  if (stale.length) {
@@ -3176,7 +3392,8 @@ ${serialised}`);
3176
3392
  console.log(` ${command}`);
3177
3393
  console.log(` Remove them from ${settingsFile} by hand; setup cannot tell them from a hook you wrote.`);
3178
3394
  }
3179
- const healthy = credentials?.toolInstallationId && registered === spec.hookEvents.length && fs.existsSync(binary) && !stale.length;
3395
+ const paired = localOnly || credentials?.toolInstallationId !== void 0;
3396
+ const healthy = paired && registered === spec.hookEvents.length && fs.existsSync(binary) && !stale.length;
3180
3397
  return healthy ? 0 : 1;
3181
3398
  }
3182
3399
  function uninstall(options, spec) {
@@ -3467,9 +3684,9 @@ var require_out2 = __commonJS({
3467
3684
  "../packages/tool-kit/out/index.js"(exports) {
3468
3685
  "use strict";
3469
3686
  Object.defineProperty(exports, "__esModule", { value: true });
3470
- exports.consumeTurnDurationMs = exports.writeTopLevelCredentials = exports.writeMachineCredentials = exports.writeHostCredentials = exports.removeHostCredentials = exports.readMachineCredentials = exports.readHostCredentials = exports.credentialsFilePath = exports.writeHookSettings = exports.runCliAgentSetup = exports.isCliAgentManagementCommand = exports.findStaleHookCommands = exports.cliAgentHookBinPath = exports.resolveContextHashes = exports.resolveCliAgentInstallationId = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.MissingInstallationIdError = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = exports.mintIdempotencyKey = exports.looksLikeCorrection = exports.outcomeForHook = exports.inferOutcome = exports.getNestedNumber = exports.getNestedString = exports.getNested = exports.getNumber = exports.getString = exports.localHourAt = exports.utcOffsetMinutesAt = exports.BUSINESS_DAY = exports.isOutsideBusinessHours = exports.isAfterHours = exports.bucketDurationMs = exports.bucketLinesChanged = exports.classifyModelClass = exports.autonomyBand = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
3471
- exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = exports.recordForgeProjectAlias = exports.forgeFullNameFromConfig = exports.readForgeFullName = exports.parseForgeFullName = exports.forgeProjectHash = exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.readBranchName = exports.normalizeBranchName = exports.deriveBranchHashForCwd = exports.deriveBranchHash = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.fixedTimeProvider = exports.systemTimeProvider = exports.enforceOutboxBounds = exports.claimOutbox = exports.readOutboxSummary = exports.appendToOutbox = exports.defaultOutboxFilePath = exports.outboxDrainEnabled = exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = exports.DEFAULT_OUTBOX_MAX_AGE_MS = exports.DEFAULT_OUTBOX_MAX_ENTRIES = exports.OUTBOX_DRAIN_ENV_VAR = exports.recordOutboxDiscard = exports.unresolvedToolInstallationId = exports.unresolvedStateFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.listPersistedToolInstallationIds = exports.defaultTokenFilePath = exports.ascendaHome = exports.recordTurnStart = void 0;
3472
- exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = void 0;
3687
+ exports.renderSetupDisclosure = exports.REFUSALS = exports.FREE_TEXT_KEYS = exports.FAMILY_SENTENCES = exports.ALWAYS_SENT = exports.writeHookSettings = exports.runCliAgentSetup = exports.isCliAgentManagementCommand = exports.findStaleHookCommands = exports.cliAgentHookBinPath = exports.resolveContextHashes = exports.resolveCliAgentInstallationId = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.MissingInstallationIdError = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = exports.describeCollectorVersion = exports.UNRELEASED_COLLECTOR_VERSION = exports.COLLECTOR_VERSION = exports.mintIdempotencyKey = exports.looksLikeCorrection = exports.outcomeForHook = exports.inferOutcome = exports.getNestedNumber = exports.getNestedString = exports.getNested = exports.getNumber = exports.getString = exports.localHourAt = exports.utcOffsetMinutesAt = exports.BUSINESS_DAY = exports.isOutsideBusinessHours = exports.isAfterHours = exports.bucketDurationMs = exports.bucketLinesChanged = exports.classifyModelClass = exports.autonomyBand = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
3688
+ exports.readForgeFullName = exports.parseForgeFullName = exports.forgeProjectHash = exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.readBranchName = exports.normalizeBranchName = exports.deriveBranchHashForCwd = exports.deriveBranchHash = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.fixedTimeProvider = exports.systemTimeProvider = exports.enforceOutboxBounds = exports.claimOutbox = exports.readOutboxSummary = exports.appendToOutbox = exports.defaultOutboxFilePath = exports.outboxDrainEnabled = exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = exports.DEFAULT_OUTBOX_MAX_AGE_MS = exports.DEFAULT_OUTBOX_MAX_ENTRIES = exports.OUTBOX_DRAIN_ENV_VAR = exports.recordOutboxDiscard = exports.unresolvedToolInstallationId = exports.unresolvedStateFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.listPersistedToolInstallationIds = exports.defaultTokenFilePath = exports.ascendaHome = exports.recordTurnStart = exports.consumeTurnDurationMs = exports.writeTopLevelCredentials = exports.writeMachineCredentials = exports.writeHostCredentials = exports.removeHostCredentials = exports.readMachineCredentials = exports.readHostCredentials = exports.isLocalOnlyHostInstall = exports.credentialsFilePath = void 0;
3689
+ exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = exports.recordForgeProjectAlias = exports.forgeFullNameFromConfig = void 0;
3473
3690
  var commandClassifier_1 = require_commandClassifier();
3474
3691
  Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
3475
3692
  return commandClassifier_1.classifyCommand;
@@ -3550,6 +3767,16 @@ var require_out2 = __commonJS({
3550
3767
  Object.defineProperty(exports, "mintIdempotencyKey", { enumerable: true, get: function() {
3551
3768
  return payload_1.mintIdempotencyKey;
3552
3769
  } });
3770
+ var collectorVersion_1 = require_collectorVersion();
3771
+ Object.defineProperty(exports, "COLLECTOR_VERSION", { enumerable: true, get: function() {
3772
+ return collectorVersion_1.COLLECTOR_VERSION;
3773
+ } });
3774
+ Object.defineProperty(exports, "UNRELEASED_COLLECTOR_VERSION", { enumerable: true, get: function() {
3775
+ return collectorVersion_1.UNRELEASED_COLLECTOR_VERSION;
3776
+ } });
3777
+ Object.defineProperty(exports, "describeCollectorVersion", { enumerable: true, get: function() {
3778
+ return collectorVersion_1.describeCollectorVersion;
3779
+ } });
3553
3780
  var eventSender_1 = require_eventSender();
3554
3781
  Object.defineProperty(exports, "AscendaEventSender", { enumerable: true, get: function() {
3555
3782
  return eventSender_1.AscendaEventSender;
@@ -3608,10 +3835,29 @@ var require_out2 = __commonJS({
3608
3835
  Object.defineProperty(exports, "writeHookSettings", { enumerable: true, get: function() {
3609
3836
  return cliAgentSetup_1.writeHookSettings;
3610
3837
  } });
3838
+ var setupDisclosure_1 = require_setupDisclosure();
3839
+ Object.defineProperty(exports, "ALWAYS_SENT", { enumerable: true, get: function() {
3840
+ return setupDisclosure_1.ALWAYS_SENT;
3841
+ } });
3842
+ Object.defineProperty(exports, "FAMILY_SENTENCES", { enumerable: true, get: function() {
3843
+ return setupDisclosure_1.FAMILY_SENTENCES;
3844
+ } });
3845
+ Object.defineProperty(exports, "FREE_TEXT_KEYS", { enumerable: true, get: function() {
3846
+ return setupDisclosure_1.FREE_TEXT_KEYS;
3847
+ } });
3848
+ Object.defineProperty(exports, "REFUSALS", { enumerable: true, get: function() {
3849
+ return setupDisclosure_1.REFUSALS;
3850
+ } });
3851
+ Object.defineProperty(exports, "renderSetupDisclosure", { enumerable: true, get: function() {
3852
+ return setupDisclosure_1.renderSetupDisclosure;
3853
+ } });
3611
3854
  var credentials_1 = require_credentials();
3612
3855
  Object.defineProperty(exports, "credentialsFilePath", { enumerable: true, get: function() {
3613
3856
  return credentials_1.credentialsFilePath;
3614
3857
  } });
3858
+ Object.defineProperty(exports, "isLocalOnlyHostInstall", { enumerable: true, get: function() {
3859
+ return credentials_1.isLocalOnlyHostInstall;
3860
+ } });
3615
3861
  Object.defineProperty(exports, "readHostCredentials", { enumerable: true, get: function() {
3616
3862
  return credentials_1.readHostCredentials;
3617
3863
  } });
@@ -3939,6 +4185,9 @@ var SETUP = {
3939
4185
  binaryName: "ascenda-gemini-hook",
3940
4186
  hookEvents: HOOK_EVENTS,
3941
4187
  restartHint: "Restart Gemini CLI to load the hooks.",
4188
+ // Nothing beyond the shared set: Gemini's hooks expose no posture, no
4189
+ // model, no git action and no context figure.
4190
+ sends: [],
3942
4191
  settings: {
3943
4192
  settingsPath: (scope, projectDir) => scope === "user" ? path.join(os.homedir(), ".gemini", "settings.json") : path.join(projectDir, ".gemini", "settings.json"),
3944
4193
  // Gemini nests one level deeper than the other agents: each event maps to
@@ -1,8 +1,7 @@
1
1
  import { bucketPromptSize, getString, inferOutcome } from "@ascenda-one/tool-kit";
2
2
  /**
3
3
  * Maps Gemini CLI hooks onto the desktop waterline's much smaller vocabulary
4
- * (`LiveBusEvent` in `@ascenda-one/tool-kit`;
5
- * `docs/MACOS_LIVE_DEMAND_WATERLINE.md` in the app repo).
4
+ * (`LiveBusEvent` in `@ascenda-one/tool-kit`).
6
5
  *
7
6
  * This is **not** the telemetry mapping and shares nothing with
8
7
  * `mapGeminiEvent`. That one feeds daily buckets read hours later; this one
@@ -1 +1 @@
1
- {"version":3,"file":"liveSignal.js","sourceRoot":"","sources":["../src/liveSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAOlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,aAAa,CAAC,QAA6B,EAAE,KAAsB;IACjF,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5C,qEAAqE;YACrE,mEAAmE;YACnE,uEAAuE;YACvE,uCAAuC;YACvC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QACpI,CAAC;QACD,KAAK,YAAY,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QACjD,KAAK,WAAW,CAAC,CAAC,OAAO,YAAY,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACnG,KAAK,aAAa,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACnD,KAAK,YAAY,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC5C,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;;;GAMG"}
1
+ {"version":3,"file":"liveSignal.js","sourceRoot":"","sources":["../src/liveSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAOlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,aAAa,CAAC,QAA6B,EAAE,KAAsB;IACjF,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5C,qEAAqE;YACrE,mEAAmE;YACnE,uEAAuE;YACvE,uCAAuC;YACvC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QACpI,CAAC;QACD,KAAK,YAAY,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QACjD,KAAK,WAAW,CAAC,CAAC,OAAO,YAAY,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACnG,KAAK,aAAa,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACnD,KAAK,YAAY,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC5C,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;;;GAMG"}
package/dist/setup.js CHANGED
@@ -22,6 +22,9 @@ export const SETUP = {
22
22
  binaryName: "ascenda-gemini-hook",
23
23
  hookEvents: HOOK_EVENTS,
24
24
  restartHint: "Restart Gemini CLI to load the hooks.",
25
+ // Nothing beyond the shared set: Gemini's hooks expose no posture, no
26
+ // model, no git action and no context figure.
27
+ sends: [],
25
28
  settings: {
26
29
  settingsPath: (scope, projectDir) => scope === "user" ? path.join(os.homedir(), ".gemini", "settings.json") : path.join(projectDir, ".gemini", "settings.json"),
27
30
  // Gemini nests one level deeper than the other agents: each event maps to
package/dist/setup.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,CAAU,CAAC;AAE1I;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAI/B,MAAM,CAAC,MAAM,KAAK,GAAsB;IACtC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,YAAY;IACzB,QAAQ,EAAE,iBAAiB;IAC3B,WAAW,EAAE,2BAA2B;IACxC,UAAU,EAAE,qBAAqB;IACjC,UAAU,EAAE,WAAW;IACvB,WAAW,EAAE,uCAAuC;IACpD,QAAQ,EAAE;QACR,YAAY,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAClC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,eAAe,CAAC;QAC5H,0EAA0E;QAC1E,sEAAsE;QACtE,2EAA2E;QAC3E,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC;QAC9F,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,MAAM,KAAK,GAAG,KAAoC,CAAC;YACnD,MAAM,OAAO,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,EAAE,OAAO,KAAK,QAAQ,CAAC,EAAE,OAAO,CAAC;YACzF,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,CAAC;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,CAAU,CAAC;AAE1I;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAI/B,MAAM,CAAC,MAAM,KAAK,GAAsB;IACtC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,YAAY;IACzB,QAAQ,EAAE,iBAAiB;IAC3B,WAAW,EAAE,2BAA2B;IACxC,UAAU,EAAE,qBAAqB;IACjC,UAAU,EAAE,WAAW;IACvB,WAAW,EAAE,uCAAuC;IACpD,sEAAsE;IACtE,8CAA8C;IAC9C,KAAK,EAAE,EAAE;IACT,QAAQ,EAAE;QACR,YAAY,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAClC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,eAAe,CAAC;QAC5H,0EAA0E;QAC1E,sEAAsE;QACtE,2EAA2E;QAC3E,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC;QAC9F,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,MAAM,KAAK,GAAG,KAAoC,CAAC;YACnD,MAAM,OAAO,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,EAAE,OAAO,KAAK,QAAQ,CAAC,EAAE,OAAO,CAAC;YACzF,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,CAAC;KACF;CACF,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "publishConfig": { "access": "public" },
3
3
  "name": "@ascenda-one/gemini-hooks",
4
- "version": "0.1.27",
4
+ "version": "0.1.28",
5
5
  "description": "Gemini CLI hooks adapter for Ascenda AI workload telemetry",
6
6
  "repository": {
7
7
  "type": "git",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "scripts": {
18
18
  "build": "tsc -p ./ && npm run bundle",
19
- "bundle": "esbuild src/cli.ts --bundle --platform=node --format=esm --banner:js=\"import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);\" --outfile=dist/cli.js",
19
+ "bundle": "node ../scripts/esbuild-collector.mjs src/cli.ts --bundle --platform=node --format=esm --banner:js=\"import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);\" --outfile=dist/cli.js",
20
20
  "watch": "tsc -w -p ./",
21
21
  "test": "node --test"
22
22
  },