@pi-unipi/unipi 2.4.1 → 2.4.2

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 (96) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/package.json +26 -22
  3. package/packages/ask-user/package.json +2 -2
  4. package/packages/autocomplete/package.json +4 -2
  5. package/packages/btw/package.json +2 -2
  6. package/packages/cocoindex/package.json +2 -2
  7. package/packages/cocoindex/tools.ts +0 -8
  8. package/packages/compactor/README.md +18 -22
  9. package/packages/compactor/package.json +3 -3
  10. package/packages/compactor/src/compaction/build-sections.ts +2 -2
  11. package/packages/compactor/src/compaction/format.ts +1 -1
  12. package/packages/compactor/src/config/presets.ts +19 -30
  13. package/packages/compactor/src/index.ts +39 -56
  14. package/packages/compactor/src/session/resume-inject.ts +27 -0
  15. package/packages/compactor/src/tools/ctx-batch-execute.ts +4 -2
  16. package/packages/compactor/src/tools/ctx-execute-file.ts +4 -2
  17. package/packages/compactor/src/tools/ctx-execute.ts +4 -2
  18. package/packages/compactor/src/tools/register.ts +22 -4
  19. package/packages/compactor/src/tui/settings-overlay.ts +9 -22
  20. package/packages/compactor/src/types.ts +24 -2
  21. package/packages/core/events.ts +13 -13
  22. package/packages/core/package.json +3 -2
  23. package/packages/footer/README.md +2 -2
  24. package/packages/footer/package.json +3 -3
  25. package/packages/footer/src/events.ts +0 -10
  26. package/packages/footer/src/segments/core.ts +0 -9
  27. package/packages/image/package.json +2 -2
  28. package/packages/info-screen/core-groups.ts +0 -14
  29. package/packages/info-screen/package.json +2 -2
  30. package/packages/input-shortcuts/package.json +2 -2
  31. package/packages/kanboard/commands.ts +0 -1
  32. package/packages/kanboard/package.json +2 -2
  33. package/packages/mcp/package.json +2 -2
  34. package/packages/memory/package.json +3 -3
  35. package/packages/memory/storage.ts +0 -18
  36. package/packages/memory/tools.ts +0 -1
  37. package/packages/milestone/hooks.ts +9 -15
  38. package/packages/milestone/package.json +2 -2
  39. package/packages/notify/README.md +2 -0
  40. package/packages/notify/events.ts +23 -13
  41. package/packages/notify/package.json +2 -2
  42. package/packages/notify/tools.ts +17 -8
  43. package/packages/notify/types.ts +5 -1
  44. package/packages/ralph/package.json +3 -3
  45. package/packages/ralph/ralph-loop.ts +10 -0
  46. package/packages/subagents/README.md +3 -1
  47. package/packages/subagents/package.json +10 -6
  48. package/packages/subagents/src/agent-manager.ts +46 -11
  49. package/packages/subagents/src/core-compat.ts +38 -0
  50. package/packages/subagents/src/global.d.ts +7 -0
  51. package/packages/subagents/src/index.ts +6 -3
  52. package/packages/subagents/src/widget.ts +0 -7
  53. package/packages/unipi/bundled.js +364 -434
  54. package/packages/updater/package.json +4 -3
  55. package/packages/utility/README.md +1 -1
  56. package/packages/utility/package.json +7 -4
  57. package/packages/utility/src/commands.ts +2 -10
  58. package/packages/utility/src/diff/theme.ts +0 -3
  59. package/packages/utility/src/tui/name-badge.ts +0 -9
  60. package/packages/web-api/package.json +2 -2
  61. package/packages/web-api/src/engine/extract.ts +0 -26
  62. package/packages/web-api/src/tools.ts +0 -13
  63. package/packages/workflow/commands.ts +13 -1
  64. package/packages/workflow/index.ts +12 -1
  65. package/packages/workflow/lifecycle.ts +43 -0
  66. package/packages/workflow/package.json +3 -2
  67. package/packages/autocomplete/index.ts +0 -7
  68. package/packages/autocomplete/src/__tests__/command-registry.audit.test.ts +0 -120
  69. package/packages/autocomplete/src/__tests__/provider.sorting.test.ts +0 -423
  70. package/packages/compactor/src/compaction/recall-scope.ts +0 -32
  71. package/packages/compactor/src/compaction/sections.ts +0 -15
  72. package/packages/compactor/src/display/bash-display.ts +0 -28
  73. package/packages/compactor/src/display/diff-presentation.ts +0 -25
  74. package/packages/compactor/src/display/pending-diff-preview.ts +0 -51
  75. package/packages/compactor/src/display/user-message-box.ts +0 -16
  76. package/packages/compactor/src/session/analytics.ts +0 -205
  77. package/packages/image/index.ts +0 -1
  78. package/packages/input-shortcuts/index.ts +0 -5
  79. package/packages/input-shortcuts/src/status.ts +0 -35
  80. package/packages/kanboard/tui/kanboard-overlay.ts +0 -300
  81. package/packages/mcp/index.ts +0 -1
  82. package/packages/memory/search.ts +0 -134
  83. package/packages/milestone/coexist.ts +0 -114
  84. package/packages/notify/src/__tests__/ask-user-prompt-message.test.ts +0 -159
  85. package/packages/notify/src/__tests__/event-bus.test.ts +0 -119
  86. package/packages/notify/src/__tests__/permission-prompt-message.test.ts +0 -172
  87. package/packages/subagents/src/__tests__/badge-generation.test.ts +0 -315
  88. package/packages/subagents/src/__tests__/config.test.ts +0 -240
  89. package/packages/subagents/src/__tests__/esc-propagation.test.ts +0 -162
  90. package/packages/subagents/src/__tests__/file-lock.test.ts +0 -244
  91. package/packages/subagents/src/__tests__/shutdown-stale-ctx.test.ts +0 -185
  92. package/packages/subagents/src/__tests__/workflow-integration.test.ts +0 -334
  93. package/packages/subagents/src/prompts.ts +0 -39
  94. package/packages/utility/index.ts +0 -1
  95. package/packages/web-api/src/tui/progress.ts +0 -168
  96. package/packages/web-api/src/tui/result.ts +0 -173
@@ -327,7 +327,7 @@ var init_events = __esm({
327
327
  UNIPI_EVENTS = {
328
328
  /** Module loaded and ready */
329
329
  MODULE_READY: "unipi:module:ready",
330
- /** Module unloading */
330
+ /** @deprecated No built-in emitter; retained for external compatibility. */
331
331
  MODULE_GONE: "unipi:module:gone",
332
332
  /** Workflow command started */
333
333
  WORKFLOW_START: "unipi:workflow:start",
@@ -339,9 +339,9 @@ var init_events = __esm({
339
339
  RALPH_LOOP_END: "unipi:ralph:loop:end",
340
340
  /** Ralph loop iteration completed */
341
341
  RALPH_ITERATION_DONE: "unipi:ralph:iteration:done",
342
- /** Request module status (for info-screen) */
342
+ /** @deprecated No built-in responders; use the info registry and /unipi:info. */
343
343
  MODULE_STATUS_REQUEST: "unipi:module:status:request",
344
- /** Module status response */
344
+ /** @deprecated No built-in aggregator; use the info registry and /unipi:info. */
345
345
  MODULE_STATUS_RESPONSE: "unipi:module:status:response",
346
346
  /** Info screen group registered */
347
347
  INFO_GROUP_REGISTERED: "unipi:info:group:registered",
@@ -351,7 +351,7 @@ var init_events = __esm({
351
351
  MEMORY_STORED: "unipi:memory:stored",
352
352
  /** Memory deleted */
353
353
  MEMORY_DELETED: "unipi:memory:deleted",
354
- /** Memory search performed */
354
+ /** @deprecated No built-in emitter; retained for external compatibility. */
355
355
  MEMORY_SEARCHED: "unipi:memory:searched",
356
356
  /** Memory consolidation completed */
357
357
  MEMORY_CONSOLIDATED: "unipi:memory:consolidated",
@@ -369,9 +369,9 @@ var init_events = __esm({
369
369
  MCP_CATALOG_SYNCED: "unipi:mcp:catalog:synced",
370
370
  /** Compactor: compaction completed */
371
371
  COMPACTOR_COMPACTED: "unipi:compactor:compacted",
372
- /** Compactor: stats updated */
372
+ /** @deprecated Footer reads live Pi session data; retained for compatibility. */
373
373
  COMPACTOR_STATS_UPDATED: "unipi:compactor:stats:updated",
374
- /** Utility module cleanup started */
374
+ /** @deprecated No built-in emitter; retained for external compatibility. */
375
375
  UTILITY_CLEANUP_START: "unipi:utility:cleanup:start",
376
376
  /** Utility module cleanup completed */
377
377
  UTILITY_CLEANUP_DONE: "unipi:utility:cleanup:done",
@@ -379,9 +379,9 @@ var init_events = __esm({
379
379
  UTILITY_DIAGNOSTICS_START: "unipi:utility:diagnostics:start",
380
380
  /** Utility diagnostics completed */
381
381
  UTILITY_DIAGNOSTICS_DONE: "unipi:utility:diagnostics:done",
382
- /** Utility cache invalidated */
382
+ /** @deprecated No built-in emitter; retained for external compatibility. */
383
383
  UTILITY_CACHE_INVALIDATED: "unipi:utility:cache:invalidated",
384
- /** Utility lifecycle state changed */
384
+ /** @deprecated No built-in emitter; retained for external compatibility. */
385
385
  UTILITY_LIFECYCLE_STATE: "unipi:utility:lifecycle:state",
386
386
  /** Notification sent */
387
387
  NOTIFICATION_SENT: "unipi:notify:sent",
@@ -397,11 +397,11 @@ var init_events = __esm({
397
397
  UPDATE_APPLIED: "unipi:update:applied",
398
398
  /** Update error */
399
399
  UPDATE_ERROR: "unipi:update:error",
400
- /** CocoIndex: update started */
400
+ /** @deprecated Reserved external contract; no built-in emitter. */
401
401
  COCOINDEX_UPDATE_STARTED: "unipi:cocoindex:update:started",
402
- /** CocoIndex: update completed */
402
+ /** @deprecated Reserved external contract; no built-in emitter. */
403
403
  COCOINDEX_UPDATE_COMPLETED: "unipi:cocoindex:update:completed",
404
- /** CocoIndex: search performed */
404
+ /** @deprecated Reserved external contract; no built-in emitter. */
405
405
  COCOINDEX_SEARCH_PERFORMED: "unipi:cocoindex:search:performed"
406
406
  };
407
407
  }
@@ -1457,21 +1457,6 @@ var init_storage = __esm({
1457
1457
  this.db = null;
1458
1458
  }
1459
1459
  }
1460
- /**
1461
- * Remove corrupted database files (db, wal, shm).
1462
- */
1463
- removeCorruptedDb() {
1464
- const dbPath = path5.join(this.scopeDir, MEMORY_DB_NAME);
1465
- const files = [dbPath, `${dbPath}-wal`, `${dbPath}-shm`];
1466
- for (const file of files) {
1467
- try {
1468
- if (fs5.existsSync(file)) {
1469
- fs5.unlinkSync(file);
1470
- }
1471
- } catch {
1472
- }
1473
- }
1474
- }
1475
1460
  /**
1476
1461
  * Check if database is healthy.
1477
1462
  */
@@ -5466,53 +5451,6 @@ var init_snapshot = __esm({
5466
5451
  }
5467
5452
  });
5468
5453
 
5469
- // packages/compactor/src/session/auto-inject.ts
5470
- var auto_inject_exports = {};
5471
- __export(auto_inject_exports, {
5472
- buildAutoInjection: () => buildAutoInjection
5473
- });
5474
- function estimateTokens(text) {
5475
- return Math.ceil(text.length / 4);
5476
- }
5477
- function buildAutoInjection(events) {
5478
- const parts = [];
5479
- let tokenBudget = MAX_TOKENS2;
5480
- const roleEvents = events.filter((e) => e.category === "rule");
5481
- if (roleEvents.length > 0) {
5482
- const directive = roleEvents[roleEvents.length - 1].data;
5483
- const directiveText = `[Role Directive]
5484
- ${directive}`;
5485
- const tokens = estimateTokens(directiveText);
5486
- if (tokens <= tokenBudget) {
5487
- parts.push(directiveText);
5488
- tokenBudget -= tokens;
5489
- }
5490
- }
5491
- if (tokenBudget > 80) {
5492
- const intentEvents = events.filter((e) => e.category === "intent");
5493
- if (intentEvents.length > 0) {
5494
- const mode = intentEvents[intentEvents.length - 1].data;
5495
- const modeText = `[Session Mode]
5496
- ${mode}`;
5497
- const tokens = estimateTokens(modeText);
5498
- if (tokens <= tokenBudget) {
5499
- parts.push(modeText);
5500
- tokenBudget -= tokens;
5501
- }
5502
- }
5503
- }
5504
- if (parts.length === 0) return null;
5505
- const text = parts.join("\n\n");
5506
- return { text, tokens: estimateTokens(text) };
5507
- }
5508
- var MAX_TOKENS2;
5509
- var init_auto_inject = __esm({
5510
- "packages/compactor/src/session/auto-inject.ts"() {
5511
- "use strict";
5512
- MAX_TOKENS2 = 150;
5513
- }
5514
- });
5515
-
5516
5454
  // packages/compactor/src/config/presets.ts
5517
5455
  import { createHash as createHash3 } from "node:crypto";
5518
5456
  function presetHash(config) {
@@ -5546,7 +5484,7 @@ function parsePreset(raw) {
5546
5484
  }
5547
5485
  return void 0;
5548
5486
  }
5549
- var pipeline, pipelineAllOn, preset, PRESET_CONFIGS, presetHashes, OLD_TO_NEW;
5487
+ var pipeline, pipelineWithAutoInjection, preset, PRESET_CONFIGS, presetHashes, OLD_TO_NEW;
5550
5488
  var init_presets = __esm({
5551
5489
  "packages/compactor/src/config/presets.ts"() {
5552
5490
  "use strict";
@@ -5556,13 +5494,8 @@ var init_presets = __esm({
5556
5494
  ...overrides,
5557
5495
  customNoisePatterns: [...overrides.customNoisePatterns ?? DEFAULT_COMPACTOR_CONFIG.pipeline.customNoisePatterns]
5558
5496
  });
5559
- pipelineAllOn = () => pipeline({
5560
- ttlCache: true,
5561
- autoInjection: true,
5562
- proximityReranking: true,
5563
- timelineSort: true,
5564
- progressiveThrottling: true,
5565
- mmapPragma: true
5497
+ pipelineWithAutoInjection = () => pipeline({
5498
+ autoInjection: true
5566
5499
  });
5567
5500
  preset = (overrides) => ({
5568
5501
  ...structuredClone(DEFAULT_COMPACTOR_CONFIG),
@@ -5574,23 +5507,23 @@ var init_presets = __esm({
5574
5507
  userPreferences: { ...DEFAULT_COMPACTOR_CONFIG.userPreferences, ...overrides.userPreferences ?? {} },
5575
5508
  briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, ...overrides.briefTranscript ?? {} },
5576
5509
  sessionContinuity: { ...DEFAULT_COMPACTOR_CONFIG.sessionContinuity, ...overrides.sessionContinuity ?? {} },
5577
- fts5Index: { ...DEFAULT_COMPACTOR_CONFIG.fts5Index, ...overrides.fts5Index ?? {} },
5510
+ // Legacy compatibility data only; presets never advertise or alter it.
5511
+ fts5Index: { ...DEFAULT_COMPACTOR_CONFIG.fts5Index },
5578
5512
  sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, ...overrides.sandboxExecution ?? {} },
5579
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, ...overrides.toolDisplay ?? {} },
5513
+ // Legacy compatibility data only; presets never advertise or alter it.
5514
+ toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay },
5580
5515
  pipeline: pipeline(overrides.pipeline),
5581
5516
  autoCompaction: { ...DEFAULT_COMPACTOR_CONFIG.autoCompaction, ...overrides.autoCompaction ?? {} }
5582
5517
  });
5583
5518
  PRESET_CONFIGS = {
5584
5519
  // New preset names
5585
5520
  precise: preset({
5586
- pipeline: pipeline({ ttlCache: true, mmapPragma: true }),
5587
- sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, mode: "safe-only" },
5588
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, mode: "opencode" }
5521
+ pipeline: pipeline(),
5522
+ sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, mode: "all" }
5589
5523
  }),
5590
5524
  thorough: preset({
5591
- pipeline: pipelineAllOn(),
5592
- briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, mode: "full" },
5593
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, mode: "verbose" }
5525
+ pipeline: pipelineWithAutoInjection(),
5526
+ briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, mode: "full" }
5594
5527
  }),
5595
5528
  lean: preset({
5596
5529
  pipeline: pipeline(),
@@ -5601,26 +5534,20 @@ var init_presets = __esm({
5601
5534
  userPreferences: { ...DEFAULT_COMPACTOR_CONFIG.userPreferences, enabled: false, mode: "off" },
5602
5535
  briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, enabled: true, mode: "minimal" },
5603
5536
  sessionContinuity: { ...DEFAULT_COMPACTOR_CONFIG.sessionContinuity, enabled: false, mode: "off" },
5604
- fts5Index: { ...DEFAULT_COMPACTOR_CONFIG.fts5Index, enabled: false, mode: "off" },
5605
- sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, enabled: false, mode: "off" },
5606
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, enabled: true, mode: "opencode" }
5537
+ sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, enabled: false, mode: "off" }
5607
5538
  }),
5608
5539
  balanced: preset({
5609
- pipeline: pipelineAllOn(),
5610
- briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, mode: "compact" },
5611
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, mode: "balanced" },
5612
- fts5Index: { ...DEFAULT_COMPACTOR_CONFIG.fts5Index, mode: "auto" }
5540
+ pipeline: pipelineWithAutoInjection(),
5541
+ briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, mode: "compact" }
5613
5542
  }),
5614
5543
  // Backward-compat aliases — map old names to new
5615
5544
  opencode: preset({
5616
- pipeline: pipeline({ ttlCache: true, mmapPragma: true }),
5617
- sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, mode: "safe-only" },
5618
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, mode: "opencode" }
5545
+ pipeline: pipeline(),
5546
+ sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, mode: "all" }
5619
5547
  }),
5620
5548
  verbose: preset({
5621
- pipeline: pipelineAllOn(),
5622
- briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, mode: "full" },
5623
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, mode: "verbose" }
5549
+ pipeline: pipelineWithAutoInjection(),
5550
+ briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, mode: "full" }
5624
5551
  }),
5625
5552
  minimal: preset({
5626
5553
  pipeline: pipeline(),
@@ -5631,9 +5558,7 @@ var init_presets = __esm({
5631
5558
  userPreferences: { ...DEFAULT_COMPACTOR_CONFIG.userPreferences, enabled: false, mode: "off" },
5632
5559
  briefTranscript: { ...DEFAULT_COMPACTOR_CONFIG.briefTranscript, enabled: true, mode: "minimal" },
5633
5560
  sessionContinuity: { ...DEFAULT_COMPACTOR_CONFIG.sessionContinuity, enabled: false, mode: "off" },
5634
- fts5Index: { ...DEFAULT_COMPACTOR_CONFIG.fts5Index, enabled: false, mode: "off" },
5635
- sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, enabled: false, mode: "off" },
5636
- toolDisplay: { ...DEFAULT_COMPACTOR_CONFIG.toolDisplay, enabled: true, mode: "opencode" }
5561
+ sandboxExecution: { ...DEFAULT_COMPACTOR_CONFIG.sandboxExecution, enabled: false, mode: "off" }
5637
5562
  }),
5638
5563
  custom: structuredClone(DEFAULT_COMPACTOR_CONFIG)
5639
5564
  };
@@ -5791,7 +5716,7 @@ var init_settings_overlay = __esm({
5791
5716
  key: "sessionContinuity",
5792
5717
  label: "Session Continuity",
5793
5718
  description: "XML resume snapshot for compaction survival",
5794
- modes: ["full", "essential-only", "off"],
5719
+ modes: ["full", "off"],
5795
5720
  getEnabled: (c) => c.sessionContinuity.enabled,
5796
5721
  setEnabled: (c, v) => c.sessionContinuity.enabled = v,
5797
5722
  getMode: (c) => c.sessionContinuity.mode,
@@ -5801,30 +5726,17 @@ var init_settings_overlay = __esm({
5801
5726
  key: "sandboxExecution",
5802
5727
  label: "Sandbox Execution",
5803
5728
  description: "Polyglot code execution",
5804
- modes: ["all", "safe-only", "off"],
5729
+ modes: ["all", "off"],
5805
5730
  getEnabled: (c) => c.sandboxExecution.enabled,
5806
5731
  setEnabled: (c, v) => c.sandboxExecution.enabled = v,
5807
5732
  getMode: (c) => c.sandboxExecution.mode,
5808
5733
  setMode: (c, v) => c.sandboxExecution.mode = v
5809
- },
5810
- {
5811
- key: "toolDisplay",
5812
- label: "Tool Display",
5813
- description: "Override tool output rendering",
5814
- modes: ["opencode", "balanced", "verbose", "custom"],
5815
- getEnabled: (c) => c.toolDisplay.enabled,
5816
- setEnabled: (c, v) => c.toolDisplay.enabled = v,
5817
- getMode: (c) => c.toolDisplay.mode,
5818
- setMode: (c, v) => c.toolDisplay.mode = v
5819
5734
  }
5820
5735
  ];
5821
5736
  PIPELINE_ITEMS = [
5822
- { key: "ttlCache", label: "TTL Cache", description: "Cache with time-based expiry", group: "On Compaction", getValue: (c) => c.pipeline.ttlCache, setValue: (c, v) => c.pipeline.ttlCache = v },
5823
- { key: "autoInjection", label: "Auto Injection", description: "Inject behavioral state after compaction", group: "On Compaction", getValue: (c) => c.pipeline.autoInjection, setValue: (c, v) => c.pipeline.autoInjection = v },
5824
- { key: "mmapPragma", label: "MMap Pragma", description: "Use mmap for SQLite I/O", group: "On Compaction", getValue: (c) => c.pipeline.mmapPragma, setValue: (c, v) => c.pipeline.mmapPragma = v },
5825
- { key: "proximityReranking", label: "Proximity Reranking", description: "Rerank search results by proximity", group: "On Search", getValue: (c) => c.pipeline.proximityReranking, setValue: (c, v) => c.pipeline.proximityReranking = v },
5826
- { key: "timelineSort", label: "Timeline Sort", description: "Sort session events chronologically", group: "On Search", getValue: (c) => c.pipeline.timelineSort, setValue: (c, v) => c.pipeline.timelineSort = v },
5827
- { key: "progressiveThrottling", label: "Progressive Throttling", description: "Slow down processing for large projects", group: "On Compaction", getValue: (c) => c.pipeline.progressiveThrottling, setValue: (c, v) => c.pipeline.progressiveThrottling = v }
5737
+ // Only expose implemented behavior. Reserved compatibility fields remain in
5738
+ // the persisted schema so existing config files continue to load.
5739
+ { key: "autoInjection", label: "Auto Injection", description: "Inject behavioral state after compaction", group: "On Compaction", getValue: (c) => c.pipeline.autoInjection, setValue: (c, v) => c.pipeline.autoInjection = v }
5828
5740
  ];
5829
5741
  PRESETS2 = ["precise", "balanced", "thorough", "lean"];
5830
5742
  THRESHOLD_VALUES = ["60%", "70%", "75%", "80%", "85%", "90%", "95%"];
@@ -5832,20 +5744,20 @@ var init_settings_overlay = __esm({
5832
5744
  REPEAT_GROWTH_VALUES = ["0", "1k", "4k", "8k", "16k", "32k"];
5833
5745
  PRESET_DESCRIPTIONS = {
5834
5746
  precise: {
5835
- summary: "Code-heavy, minimal waste \u2014 compaction: full, sandbox: safe-only",
5836
- detail: "Max token savings. Compaction: full. Display: minimal.\nSandbox: safe-only. Pipeline: ttlCache+mmap on."
5747
+ summary: "Code-heavy, minimal waste \u2014 compaction: full, sandbox: all",
5748
+ detail: "Max token savings. Compaction: full.\nSandbox: all. Auto injection: off."
5837
5749
  },
5838
5750
  balanced: {
5839
5751
  summary: "Daily use (default) \u2014 all strategies moderate",
5840
- detail: "Moderate all strategies. Display: balanced.\nSandbox: all. Pipeline: all 6 on."
5752
+ detail: "Moderate all strategies.\nSandbox: all. Auto injection: on."
5841
5753
  },
5842
5754
  thorough: {
5843
5755
  summary: "Debug/audit \u2014 everything on, full transcript",
5844
- detail: "Everything enabled. Display: verbose.\nSandbox: all. Pipeline: all 6 on."
5756
+ detail: "Everything enabled.\nSandbox: all. Auto injection: on."
5845
5757
  },
5846
5758
  lean: {
5847
5759
  summary: "Quick fixes, short sessions \u2014 compaction only",
5848
- detail: "Compaction only. Display: opencode.\nSandbox: off. Pipeline: all 6 off."
5760
+ detail: "Compaction only.\nSandbox: off. Auto injection: off."
5849
5761
  }
5850
5762
  };
5851
5763
  THEME = {
@@ -6571,137 +6483,6 @@ var init_scanner = __esm({
6571
6483
  }
6572
6484
  });
6573
6485
 
6574
- // packages/compactor/src/display/render-utils.ts
6575
- function splitLines(text) {
6576
- return text.split(/\r?\n/);
6577
- }
6578
- function countNonEmptyLines(text) {
6579
- return splitLines(text).filter((l) => l.trim()).length;
6580
- }
6581
- function previewLines(text, lines) {
6582
- const all = splitLines(text);
6583
- if (all.length <= lines) return text;
6584
- return all.slice(0, lines).join("\n") + `
6585
- ...(${all.length - lines} more lines)`;
6586
- }
6587
- function shortenPath(path28, maxLen = 60) {
6588
- if (path28.length <= maxLen) return path28;
6589
- const parts = path28.split("/");
6590
- if (parts.length <= 2) return "..." + path28.slice(-(maxLen - 3));
6591
- return parts[0] + "/.../" + parts.slice(-2).join("/");
6592
- }
6593
- var init_render_utils = __esm({
6594
- "packages/compactor/src/display/render-utils.ts"() {
6595
- "use strict";
6596
- }
6597
- });
6598
-
6599
- // packages/compactor/src/display/tool-overrides.ts
6600
- var tool_overrides_exports = {};
6601
- __export(tool_overrides_exports, {
6602
- applyToolDisplayOverride: () => applyToolDisplayOverride,
6603
- renderBashResult: () => renderBashResult,
6604
- renderReadResult: () => renderReadResult,
6605
- renderSearchResult: () => renderSearchResult
6606
- });
6607
- function renderReadResult(content, filePath, config) {
6608
- switch (config.readOutputMode) {
6609
- case "hidden":
6610
- return `[Read: ${shortenPath(filePath)}]`;
6611
- case "summary":
6612
- return `[Read: ${shortenPath(filePath)} \u2014 ${countNonEmptyLines(content)} lines]`;
6613
- case "preview":
6614
- return previewLines(content, config.previewLines ?? 20);
6615
- default:
6616
- return content;
6617
- }
6618
- }
6619
- function renderSearchResult(results, config) {
6620
- switch (config.searchOutputMode) {
6621
- case "hidden":
6622
- return `[Search results hidden]`;
6623
- case "count":
6624
- return `[Search: ${countNonEmptyLines(results)} matches]`;
6625
- case "preview":
6626
- return previewLines(results, config.previewLines ?? 20);
6627
- default:
6628
- return results;
6629
- }
6630
- }
6631
- function renderBashResult(output, command, config) {
6632
- switch (config.bashOutputMode) {
6633
- case "hidden":
6634
- return `[Bash: ${command.slice(0, 60)}]`;
6635
- case "summary":
6636
- return `[Bash: ${command.slice(0, 60)} \u2014 ${countNonEmptyLines(output)} lines]`;
6637
- case "preview":
6638
- return previewLines(output, config.bashCollapsedLines ?? 5);
6639
- default:
6640
- return output;
6641
- }
6642
- }
6643
- function applyToolDisplayOverride(toolName, event, config) {
6644
- const content = event.content;
6645
- if (!content || !Array.isArray(content) || content.length === 0) return void 0;
6646
- const textContent = content[0]?.text ?? "";
6647
- const previewLinesCount = config.previewLines ?? 20;
6648
- const collapsedLines = config.bashCollapsedLines ?? 5;
6649
- let overridden;
6650
- switch (toolName) {
6651
- case "read": {
6652
- const filePath = String(event.args?.path ?? "");
6653
- const mode = config.readOutputMode ?? "full";
6654
- if (mode === "hidden") {
6655
- overridden = `[Read: ${shortenPath(filePath)}]`;
6656
- } else if (mode === "summary") {
6657
- overridden = `[Read: ${shortenPath(filePath)} \u2014 ${countNonEmptyLines(textContent)} lines]`;
6658
- } else if (mode === "preview") {
6659
- overridden = previewLines(textContent, previewLinesCount);
6660
- }
6661
- break;
6662
- }
6663
- case "grep":
6664
- case "find":
6665
- case "ls": {
6666
- const mode = config.searchOutputMode ?? "full";
6667
- if (mode === "hidden") {
6668
- overridden = `[${toolName} results hidden]`;
6669
- } else if (mode === "count") {
6670
- overridden = `[${toolName}: ${countNonEmptyLines(textContent)} matches]`;
6671
- } else if (mode === "preview") {
6672
- overridden = previewLines(textContent, previewLinesCount);
6673
- }
6674
- break;
6675
- }
6676
- case "bash": {
6677
- const command = String(event.args?.command ?? "");
6678
- const mode = config.bashOutputMode ?? "full";
6679
- if (mode === "hidden") {
6680
- overridden = `[Bash: ${command.slice(0, 60)}]`;
6681
- } else if (mode === "summary") {
6682
- overridden = `[Bash: ${command.slice(0, 60)} \u2014 ${countNonEmptyLines(textContent)} lines]`;
6683
- } else if (mode === "preview") {
6684
- overridden = previewLines(textContent, collapsedLines);
6685
- }
6686
- break;
6687
- }
6688
- default:
6689
- return void 0;
6690
- }
6691
- if (overridden !== void 0) {
6692
- return {
6693
- content: [{ type: "text", text: overridden }]
6694
- };
6695
- }
6696
- return void 0;
6697
- }
6698
- var init_tool_overrides = __esm({
6699
- "packages/compactor/src/display/tool-overrides.ts"() {
6700
- "use strict";
6701
- init_render_utils();
6702
- }
6703
- });
6704
-
6705
6486
  // packages/compactor/src/display/diff-width-safety.ts
6706
6487
  var diff_width_safety_exports = {};
6707
6488
  __export(diff_width_safety_exports, {
@@ -7038,6 +6819,15 @@ function registerWorkflowCommands(pi, options) {
7038
6819
  return null;
7039
6820
  },
7040
6821
  handler: async (args, ctx) => {
6822
+ const workflowEvent = {
6823
+ command: cmd.name,
6824
+ fullCommand: `/${fullCommand}`,
6825
+ args: args?.trim() ?? ""
6826
+ };
6827
+ if (!options.startWorkflow(workflowEvent)) {
6828
+ if (ctx.hasUI) ctx.ui.notify("Another UniPi workflow is still active", "warning");
6829
+ return;
6830
+ }
7041
6831
  const currentTools = options.getActiveTools();
7042
6832
  options.saveTools(currentTools);
7043
6833
  const sandboxTools = getToolsForCommand(cmd.name, currentTools);
@@ -7106,6 +6896,37 @@ ${taskContent}`,
7106
6896
  });
7107
6897
  }
7108
6898
 
6899
+ // packages/workflow/lifecycle.ts
6900
+ var WorkflowLifecycle = class {
6901
+ constructor(now2 = Date.now) {
6902
+ this.now = now2;
6903
+ }
6904
+ now;
6905
+ active = null;
6906
+ start(event) {
6907
+ if (this.active) return false;
6908
+ this.active = { ...event, startedAt: this.now() };
6909
+ return true;
6910
+ }
6911
+ complete(messages) {
6912
+ if (!this.active) return void 0;
6913
+ const workflow = this.active;
6914
+ this.active = null;
6915
+ const finalAssistant = [...messages].reverse().find((message) => message.role === "assistant");
6916
+ const success = finalAssistant?.stopReason !== "error" && finalAssistant?.stopReason !== "aborted";
6917
+ return {
6918
+ command: workflow.command,
6919
+ fullCommand: workflow.fullCommand,
6920
+ args: workflow.args,
6921
+ success,
6922
+ durationMs: this.now() - workflow.startedAt
6923
+ };
6924
+ }
6925
+ reset() {
6926
+ this.active = null;
6927
+ }
6928
+ };
6929
+
7109
6930
  // packages/workflow/index.ts
7110
6931
  var VERSION = getPackageVersion(dirname2(fileURLToPath(import.meta.url)));
7111
6932
  var ralphDetected = false;
@@ -7114,6 +6935,7 @@ var sandboxActive = false;
7114
6935
  var currentSandboxLevel = null;
7115
6936
  var currentSandboxTools = null;
7116
6937
  function workflow_default(pi) {
6938
+ const workflowLifecycle = new WorkflowLifecycle();
7117
6939
  registerWorkflowCommands(pi, {
7118
6940
  isRalphDetected: () => ralphDetected,
7119
6941
  getActiveTools: () => pi.getActiveTools(),
@@ -7125,6 +6947,11 @@ function workflow_default(pi) {
7125
6947
  },
7126
6948
  saveTools: (tools) => {
7127
6949
  savedTools = tools;
6950
+ },
6951
+ startWorkflow: (event) => {
6952
+ if (!workflowLifecycle.start(event)) return false;
6953
+ emitEvent(pi, UNIPI_EVENTS.WORKFLOW_START, event);
6954
+ return true;
7128
6955
  }
7129
6956
  });
7130
6957
  pi.on("tool_call", async (event, _ctx) => {
@@ -7176,7 +7003,7 @@ If the user requires an action that requires a blocked tool, respond that you do
7176
7003
  </sandbox>`
7177
7004
  };
7178
7005
  });
7179
- pi.on("agent_end", async (_event, _ctx) => {
7006
+ pi.on("agent_end", async (event, _ctx) => {
7180
7007
  if (sandboxActive && savedTools) {
7181
7008
  pi.setActiveTools(savedTools);
7182
7009
  savedTools = null;
@@ -7184,6 +7011,8 @@ If the user requires an action that requires a blocked tool, respond that you do
7184
7011
  currentSandboxLevel = null;
7185
7012
  currentSandboxTools = null;
7186
7013
  }
7014
+ const completedWorkflow = workflowLifecycle.complete(event.messages);
7015
+ if (completedWorkflow) emitEvent(pi, UNIPI_EVENTS.WORKFLOW_END, completedWorkflow);
7187
7016
  });
7188
7017
  pi.on("session_start", async (_event, ctx) => {
7189
7018
  initUnipiDirs();
@@ -7215,6 +7044,7 @@ If the user requires an action that requires a blocked tool, respond that you do
7215
7044
  ralphDetected = false;
7216
7045
  savedTools = null;
7217
7046
  sandboxActive = false;
7047
+ workflowLifecycle.reset();
7218
7048
  });
7219
7049
  }
7220
7050
 
@@ -7480,12 +7310,20 @@ Use /unipi:ralph-resume <name> to continue`, "info");
7480
7310
  );
7481
7311
  return null;
7482
7312
  }
7313
+ this.emitIterationDone(state);
7483
7314
  const needsReflection = state.reflectEvery > 0 && (state.iteration - 1) % state.reflectEvery === 0;
7484
7315
  if (needsReflection) state.lastReflectionAt = state.iteration;
7485
7316
  this.saveState(state);
7486
7317
  this.updateUI();
7487
7318
  return { state, needsReflection };
7488
7319
  }
7320
+ emitIterationDone(state) {
7321
+ this.emitFn(UNIPI_EVENTS.RALPH_ITERATION_DONE, {
7322
+ name: state.name,
7323
+ iteration: state.iteration - 1,
7324
+ nextIteration: state.iteration
7325
+ });
7326
+ }
7489
7327
  archiveLoop(name) {
7490
7328
  const state = this.loadState(name);
7491
7329
  if (!state) return false;
@@ -10735,7 +10573,6 @@ function info_screen_default(pi) {
10735
10573
  }
10736
10574
 
10737
10575
  // packages/subagents/src/index.ts
10738
- init_core();
10739
10576
  import { defineTool } from "@earendil-works/pi-coding-agent";
10740
10577
  import { Text } from "@earendil-works/pi-tui";
10741
10578
  import { Type as Type3 } from "typebox";
@@ -10743,6 +10580,29 @@ import { existsSync as existsSync13, readdirSync as readdirSync7 } from "node:fs
10743
10580
  import { join as join13 } from "node:path";
10744
10581
  import { homedir as homedir9 } from "node:os";
10745
10582
 
10583
+ // packages/subagents/src/core-compat.ts
10584
+ var MODULES2 = { SUBAGENTS: "subagents" };
10585
+ var UNIPI_EVENTS2 = {
10586
+ MODULE_READY: "unipi:module:ready",
10587
+ BADGE_GENERATE_REQUEST: "unipi:badge:generate:request"
10588
+ };
10589
+ function emitEvent2(pi, eventName, payload) {
10590
+ try {
10591
+ pi.events.emit(eventName, payload);
10592
+ return true;
10593
+ } catch {
10594
+ return false;
10595
+ }
10596
+ }
10597
+ async function withHerdrBlocked2(pi, label, fn) {
10598
+ emitEvent2(pi, "herdr:blocked", { active: true, label });
10599
+ try {
10600
+ return await fn();
10601
+ } finally {
10602
+ emitEvent2(pi, "herdr:blocked", { active: false, label });
10603
+ }
10604
+ }
10605
+
10746
10606
  // packages/subagents/src/agent-manager.ts
10747
10607
  import { randomUUID } from "node:crypto";
10748
10608
 
@@ -11124,23 +10984,43 @@ var AgentManager = class {
11124
10984
  onStart;
11125
10985
  maxConcurrent;
11126
10986
  customAgents;
10987
+ typeSettings;
11127
10988
  /** Per-file transparent locking for write agents. */
11128
10989
  fileLock = new FileLock();
11129
10990
  /** Queue of background agents waiting to start. */
11130
10991
  queue = [];
11131
10992
  /** Number of currently running background agents. */
11132
10993
  runningBackground = 0;
11133
- constructor(onComplete, maxConcurrent = DEFAULT_MAX_CONCURRENT, onStart) {
10994
+ constructor(onComplete, maxConcurrent = DEFAULT_MAX_CONCURRENT, onStart, typeSettings = {}, agentsCwd = process.cwd()) {
11134
10995
  this.onComplete = onComplete;
11135
10996
  this.onStart = onStart;
11136
10997
  this.maxConcurrent = maxConcurrent;
11137
- this.customAgents = loadCustomAgents(process.cwd());
10998
+ this.typeSettings = typeSettings;
10999
+ this.customAgents = loadCustomAgents(agentsCwd);
11138
11000
  this.cleanupInterval = setInterval(() => this.cleanup(), 6e4);
11139
11001
  }
11140
11002
  /** Get resolved agent config for a type. */
11141
11003
  getAgentConfig(type) {
11142
11004
  return this.customAgents.get(type) ?? BUILTIN_CONFIGS[type];
11143
11005
  }
11006
+ /** Public agent types known from built-ins, custom files, or JSON config. */
11007
+ getKnownTypes() {
11008
+ return [.../* @__PURE__ */ new Set([
11009
+ ...Object.keys(BUILTIN_CONFIGS).filter((type) => type !== "name-gen"),
11010
+ ...this.customAgents.keys(),
11011
+ ...Object.keys(this.typeSettings)
11012
+ ])];
11013
+ }
11014
+ /** A type is enabled only when both JSON config and agent frontmatter allow it. */
11015
+ isTypeEnabled(type) {
11016
+ const agentConfig = this.getAgentConfig(type);
11017
+ return this.typeSettings[type]?.enabled !== false && agentConfig?.enabled !== false;
11018
+ }
11019
+ assertTypeEnabled(type) {
11020
+ if (!this.isTypeEnabled(type)) {
11021
+ throw new Error(`Agent type "${type}" is disabled by configuration.`);
11022
+ }
11023
+ }
11144
11024
  setMaxConcurrent(n) {
11145
11025
  this.maxConcurrent = Math.max(1, n);
11146
11026
  this.drainQueue();
@@ -11152,6 +11032,7 @@ var AgentManager = class {
11152
11032
  * Spawn an agent. Returns ID immediately for background, waits for foreground.
11153
11033
  */
11154
11034
  spawn(pi, ctx, type, prompt, options) {
11035
+ this.assertTypeEnabled(type);
11155
11036
  const id = randomUUID().slice(0, 17);
11156
11037
  const abortController = new AbortController();
11157
11038
  const record = {
@@ -11179,9 +11060,11 @@ var AgentManager = class {
11179
11060
  record.startedAt = Date.now();
11180
11061
  if (options.isBackground) this.runningBackground++;
11181
11062
  this.onStart?.(record);
11063
+ const agentConfig = this.getAgentConfig(type);
11182
11064
  let model = options.model;
11183
- if (options.modelInput && options.modelRegistry) {
11184
- const resolved = resolveModel(options.modelInput, options.modelRegistry);
11065
+ const modelInput = options.modelInput ?? agentConfig?.model;
11066
+ if (!model && modelInput && options.modelRegistry) {
11067
+ const resolved = resolveModel(modelInput, options.modelRegistry);
11185
11068
  if (typeof resolved === "string") {
11186
11069
  record.status = "error";
11187
11070
  record.error = resolved;
@@ -11194,15 +11077,14 @@ var AgentManager = class {
11194
11077
  }
11195
11078
  model = resolved;
11196
11079
  }
11197
- const agentConfig = this.getAgentConfig(type);
11198
11080
  const promise = runAgent(ctx, type, prompt, {
11199
11081
  pi,
11200
11082
  model,
11201
11083
  agentConfig,
11202
- maxTurns: options.maxTurns,
11203
- isolated: options.isolated,
11204
- inheritContext: options.inheritContext,
11205
- thinkingLevel: options.thinkingLevel,
11084
+ maxTurns: options.maxTurns ?? agentConfig?.maxTurns,
11085
+ isolated: options.isolated ?? agentConfig?.isolated,
11086
+ inheritContext: options.inheritContext ?? agentConfig?.inheritContext,
11087
+ thinkingLevel: options.thinkingLevel ?? agentConfig?.thinking,
11206
11088
  signal: record.abortController.signal,
11207
11089
  onToolActivity: (activity) => {
11208
11090
  if (activity.type === "end") record.toolUses++;
@@ -12142,7 +12024,8 @@ function src_default(pi) {
12142
12024
  type: record.type,
12143
12025
  description: record.description
12144
12026
  });
12145
- }
12027
+ },
12028
+ config.types
12146
12029
  );
12147
12030
  function buildNotificationDetails(record, activity) {
12148
12031
  return {
@@ -12245,14 +12128,14 @@ function src_default(pi) {
12245
12128
  let sessionCtx2 = null;
12246
12129
  pi.on("session_start", async (_event, ctx) => {
12247
12130
  sessionCtx2 = ctx;
12248
- emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
12249
- name: MODULES.SUBAGENTS || "subagents",
12131
+ emitEvent2(pi, UNIPI_EVENTS2.MODULE_READY, {
12132
+ name: MODULES2.SUBAGENTS || "subagents",
12250
12133
  version: "0.2.0",
12251
12134
  commands: [],
12252
12135
  tools: ["spawn_helper", "get_helper_result"]
12253
12136
  });
12254
12137
  });
12255
- pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST, async (data) => {
12138
+ pi.events.on(UNIPI_EVENTS2.BADGE_GENERATE_REQUEST, async (data) => {
12256
12139
  const event = data;
12257
12140
  if (!sessionCtx2) return;
12258
12141
  const summary = event?.conversationSummary ?? "";
@@ -12338,13 +12221,15 @@ ${summary}` : `Generate a concise session title (MAX 5 WORDS) for this session.
12338
12221
  return { state, callbacks };
12339
12222
  }
12340
12223
  const builtinTypes = BUILTIN_TYPES.join(", ");
12224
+ const enabledTypes = manager2.getKnownTypes().filter((type) => manager2.isTypeEnabled(type));
12225
+ const availableTypes = enabledTypes.join(", ") || "none";
12341
12226
  pi.registerTool(
12342
12227
  defineTool({
12343
12228
  name: "spawn_helper",
12344
12229
  label: "Spawn Helper",
12345
12230
  description: `Launch a sub-agent for parallel work.
12346
12231
 
12347
- Available agent types: ${builtinTypes}
12232
+ Available agent types: ${availableTypes}
12348
12233
  Custom types can be defined in:
12349
12234
  - ~/.unipi/config/agents/<name>.md (global)
12350
12235
  - <workspace>/.unipi/config/agents/<name>.md (project)
@@ -12357,7 +12242,7 @@ Guidelines:
12357
12242
  - Agents inherit the parent model by default`,
12358
12243
  parameters: Type3.Object({
12359
12244
  type: Type3.String({
12360
- description: `Agent type: ${builtinTypes}, or custom type from ~/.unipi/config/agents/*.md`
12245
+ description: `Enabled agent type: ${availableTypes}`
12361
12246
  }),
12362
12247
  prompt: Type3.String({
12363
12248
  description: "The task for the agent to perform."
@@ -12600,7 +12485,7 @@ Use get_result to retrieve full results.`,
12600
12485
  }
12601
12486
  if (params.view && record.session) {
12602
12487
  const activity = agentActivity.get(record.id);
12603
- await withHerdrBlocked(
12488
+ await withHerdrBlocked2(
12604
12489
  pi,
12605
12490
  "helper viewer",
12606
12491
  () => ctx.ui.custom(
@@ -17587,7 +17472,6 @@ var PRESETS = {
17587
17472
  }
17588
17473
  }
17589
17474
  };
17590
- var HARDCODED_FALLBACK = PRESETS.default.colors;
17591
17475
  function getPreset(name) {
17592
17476
  return PRESETS[name] ?? PRESETS.default;
17593
17477
  }
@@ -18242,17 +18126,9 @@ function registerUtilityCommands(pi) {
18242
18126
  }
18243
18127
  return;
18244
18128
  }
18245
- const requestId = `status-${Date.now()}`;
18246
- emitEvent(pi, UNIPI_EVENTS.MODULE_STATUS_REQUEST, { requestId });
18247
- await new Promise((resolve13) => setTimeout(resolve13, 500));
18248
18129
  sendResponse(
18249
18130
  pi,
18250
- `## \u{1F4CA} Module Status
18251
-
18252
- Status request broadcast to all modules.
18253
- Modules that support status reporting will respond via events.
18254
-
18255
- *Request ID: \`${requestId}\`*`
18131
+ "## \u{1F4CA} Module Status\n\nUse `/unipi:info` for the live module and tool dashboard.\nUse `/unipi:doctor` to run configuration and runtime diagnostics."
18256
18132
  );
18257
18133
  }
18258
18134
  });
@@ -23731,7 +23607,7 @@ function getEnabledPlatforms(config, ntfyEnabled) {
23731
23607
  function unregisterEventListeners() {
23732
23608
  unregisterAll();
23733
23609
  }
23734
- async function dispatchNotification(pi, title, message, eventPlatforms, eventType, config, cwd) {
23610
+ async function dispatchNotification(pi, title, message, eventPlatforms, eventType, config, cwd, priority) {
23735
23611
  const ntfyConfig = loadNtfyConfig(cwd);
23736
23612
  const platforms = eventPlatforms.length > 0 ? eventPlatforms : getEnabledPlatforms(config, ntfyConfig.enabled).length > 0 ? getEnabledPlatforms(config, ntfyConfig.enabled) : config.defaultPlatforms;
23737
23613
  const enabledPlatforms = platforms.filter((p) => {
@@ -23744,8 +23620,8 @@ async function dispatchNotification(pi, title, message, eventPlatforms, eventTyp
23744
23620
  const results = await Promise.all(
23745
23621
  enabledPlatforms.map(async (platform) => {
23746
23622
  try {
23747
- await sendToPlatform(platform, title, message, config, cwd);
23748
- return { platform, success: true };
23623
+ const effectivePriority = await sendToPlatform(platform, title, message, config, cwd, priority);
23624
+ return { platform, success: true, ...effectivePriority === void 0 ? {} : { priority: effectivePriority } };
23749
23625
  } catch (err) {
23750
23626
  if (err instanceof SuppressedError2) {
23751
23627
  return { platform, success: true, suppressed: true };
@@ -23770,14 +23646,21 @@ async function dispatchNotification(pi, title, message, eventPlatforms, eventTyp
23770
23646
  });
23771
23647
  return { results, allSuccess };
23772
23648
  }
23773
- async function sendToPlatform(platform, title, message, config, cwd) {
23649
+ function mapNotifyPriority(platform, priority) {
23650
+ const mappings = {
23651
+ gotify: { low: 2, normal: 5, high: 8 },
23652
+ ntfy: { low: 2, normal: 3, high: 5 }
23653
+ };
23654
+ return mappings[platform][priority];
23655
+ }
23656
+ async function sendToPlatform(platform, title, message, config, cwd, priority) {
23774
23657
  switch (platform) {
23775
23658
  case "native":
23776
23659
  await sendNativeNotification(title, message, {
23777
23660
  windowsAppId: config.native.windowsAppId,
23778
23661
  suppressWhenFocused: config.native.suppressWhenFocused
23779
23662
  });
23780
- break;
23663
+ return void 0;
23781
23664
  case "gotify":
23782
23665
  if (!config.gotify.serverUrl || !config.gotify.appToken) {
23783
23666
  throw new Error("Gotify: serverUrl and appToken are required");
@@ -23787,9 +23670,9 @@ async function sendToPlatform(platform, title, message, config, cwd) {
23787
23670
  config.gotify.appToken,
23788
23671
  title,
23789
23672
  message,
23790
- config.gotify.priority
23673
+ priority ? mapNotifyPriority("gotify", priority) : config.gotify.priority
23791
23674
  );
23792
- break;
23675
+ return priority ? mapNotifyPriority("gotify", priority) : config.gotify.priority;
23793
23676
  case "telegram":
23794
23677
  if (!config.telegram.botToken || !config.telegram.chatId) {
23795
23678
  throw new Error("Telegram: botToken and chatId are required");
@@ -23800,10 +23683,10 @@ async function sendToPlatform(platform, title, message, config, cwd) {
23800
23683
  title,
23801
23684
  message
23802
23685
  );
23803
- break;
23686
+ return void 0;
23804
23687
  case "ntfy": {
23805
23688
  const ntfyConfig = loadNtfyConfig(cwd);
23806
- if (!ntfyConfig.enabled) return;
23689
+ if (!ntfyConfig.enabled) return void 0;
23807
23690
  if (!ntfyConfig.serverUrl || !ntfyConfig.topic) {
23808
23691
  throw new Error("ntfy: serverUrl and topic are required");
23809
23692
  }
@@ -23812,10 +23695,10 @@ async function sendToPlatform(platform, title, message, config, cwd) {
23812
23695
  ntfyConfig.topic,
23813
23696
  title,
23814
23697
  message,
23815
- ntfyConfig.priority,
23698
+ priority ? mapNotifyPriority("ntfy", priority) : ntfyConfig.priority,
23816
23699
  ntfyConfig.token
23817
23700
  );
23818
- break;
23701
+ return priority ? mapNotifyPriority("ntfy", priority) : ntfyConfig.priority;
23819
23702
  }
23820
23703
  }
23821
23704
  }
@@ -23944,7 +23827,6 @@ var NotifyUserSchema = Type6.Object({
23944
23827
  priority: Type6.Optional(
23945
23828
  Type6.String({
23946
23829
  enum: ["low", "normal", "high"],
23947
- default: "normal",
23948
23830
  description: "Priority level"
23949
23831
  })
23950
23832
  ),
@@ -23965,23 +23847,31 @@ function registerNotifyTools(pi) {
23965
23847
  const {
23966
23848
  message,
23967
23849
  title,
23968
- priority: _priority,
23850
+ priority,
23969
23851
  platforms
23970
23852
  } = params;
23971
23853
  const config = loadConfig2();
23972
23854
  const notifTitle = title || "Pi Notification";
23973
23855
  const notifPlatforms = platforms || config.defaultPlatforms;
23974
23856
  const cwd = process.cwd();
23975
- dispatchNotification(
23857
+ const dispatchPromise = dispatchNotification(
23976
23858
  pi,
23977
23859
  notifTitle,
23978
23860
  message,
23979
23861
  notifPlatforms,
23980
23862
  "agent_tool",
23981
23863
  config,
23982
- cwd
23983
- ).catch(() => {
23864
+ cwd,
23865
+ priority
23866
+ );
23867
+ void dispatchPromise.catch(() => {
23984
23868
  });
23869
+ const priorityByPlatform = priority ? Object.fromEntries(
23870
+ notifPlatforms.flatMap((platform) => {
23871
+ if (platform === "gotify" || platform === "ntfy") return [[platform, mapNotifyPriority(platform, priority)]];
23872
+ return [];
23873
+ })
23874
+ ) : void 0;
23985
23875
  return {
23986
23876
  content: [
23987
23877
  {
@@ -23989,7 +23879,7 @@ function registerNotifyTools(pi) {
23989
23879
  text: `Notification sending to ${notifPlatforms.length} platform(s): ${notifPlatforms.join(", ")}`
23990
23880
  }
23991
23881
  ],
23992
- details: { platforms: notifPlatforms }
23882
+ details: { platforms: notifPlatforms, priority, priorityByPlatform }
23993
23883
  };
23994
23884
  }
23995
23885
  });
@@ -26388,10 +26278,7 @@ function registerSessionEndHook(pi) {
26388
26278
  }
26389
26279
  }
26390
26280
  });
26391
- pi.on("input", (event) => {
26392
- return void 0;
26393
- });
26394
- pi.on("session_shutdown", () => {
26281
+ const syncModifiedDocs = () => {
26395
26282
  const cwd = process.cwd();
26396
26283
  const milestonesPath = path13.join(cwd, MILESTONE_DIRS.MILESTONES);
26397
26284
  if (!fs14.existsSync(milestonesPath)) return;
@@ -26409,7 +26296,9 @@ function registerSessionEndHook(pi) {
26409
26296
  updateItemStatus(milestonesPath, phase, text, true);
26410
26297
  }
26411
26298
  }
26412
- });
26299
+ };
26300
+ pi.events.on(UNIPI_EVENTS.WORKFLOW_END, syncModifiedDocs);
26301
+ pi.on("session_shutdown", syncModifiedDocs);
26413
26302
  }
26414
26303
 
26415
26304
  // packages/milestone/commands.ts
@@ -27979,8 +27868,49 @@ function extractEventsFromToolResult(result) {
27979
27868
 
27980
27869
  // packages/compactor/src/session/resume-inject.ts
27981
27870
  init_snapshot();
27982
- init_auto_inject();
27871
+
27872
+ // packages/compactor/src/session/auto-inject.ts
27873
+ var MAX_TOKENS2 = 150;
27874
+ function estimateTokens(text) {
27875
+ return Math.ceil(text.length / 4);
27876
+ }
27877
+ function buildAutoInjection(events) {
27878
+ const parts = [];
27879
+ let tokenBudget = MAX_TOKENS2;
27880
+ const roleEvents = events.filter((e) => e.category === "rule");
27881
+ if (roleEvents.length > 0) {
27882
+ const directive = roleEvents[roleEvents.length - 1].data;
27883
+ const directiveText = `[Role Directive]
27884
+ ${directive}`;
27885
+ const tokens = estimateTokens(directiveText);
27886
+ if (tokens <= tokenBudget) {
27887
+ parts.push(directiveText);
27888
+ tokenBudget -= tokens;
27889
+ }
27890
+ }
27891
+ if (tokenBudget > 80) {
27892
+ const intentEvents = events.filter((e) => e.category === "intent");
27893
+ if (intentEvents.length > 0) {
27894
+ const mode = intentEvents[intentEvents.length - 1].data;
27895
+ const modeText = `[Session Mode]
27896
+ ${mode}`;
27897
+ const tokens = estimateTokens(modeText);
27898
+ if (tokens <= tokenBudget) {
27899
+ parts.push(modeText);
27900
+ tokenBudget -= tokens;
27901
+ }
27902
+ }
27903
+ }
27904
+ if (parts.length === 0) return null;
27905
+ const text = parts.join("\n\n");
27906
+ return { text, tokens: estimateTokens(text) };
27907
+ }
27908
+
27909
+ // packages/compactor/src/session/resume-inject.ts
27983
27910
  init_manager();
27911
+ function isSessionContinuityEnabled(config) {
27912
+ return config.sessionContinuity.enabled && config.sessionContinuity.mode !== "off";
27913
+ }
27984
27914
  async function injectResumeSnapshot(db, sessionId, opts) {
27985
27915
  const resume = db.getResume(sessionId);
27986
27916
  if (!resume || resume.consumed) return null;
@@ -28003,6 +27933,17 @@ ${autoInjection}`;
28003
27933
  db.markResumeConsumed(sessionId);
28004
27934
  return fullSnapshot;
28005
27935
  }
27936
+ async function buildResumeContextMessage(db, sessionId) {
27937
+ const snapshot = await injectResumeSnapshot(db, sessionId);
27938
+ if (!snapshot) return void 0;
27939
+ return {
27940
+ message: {
27941
+ customType: "unipi-compactor-resume",
27942
+ content: snapshot,
27943
+ display: false
27944
+ }
27945
+ };
27946
+ }
28006
27947
 
28007
27948
  // packages/compactor/src/executor/executor.ts
28008
27949
  import { spawn as spawn3, execSync, execFileSync as execFileSync2 } from "node:child_process";
@@ -28811,8 +28752,7 @@ function compactTool() {
28811
28752
  }
28812
28753
 
28813
28754
  // packages/compactor/src/tools/ctx-execute.ts
28814
- async function ctxExecute(input) {
28815
- const executor = new PolyglotExecutor();
28755
+ async function ctxExecute(input, executor = new PolyglotExecutor()) {
28816
28756
  return executor.execute({
28817
28757
  language: input.language,
28818
28758
  code: input.code,
@@ -28822,12 +28762,11 @@ async function ctxExecute(input) {
28822
28762
 
28823
28763
  // packages/compactor/src/tools/ctx-execute-file.ts
28824
28764
  import { readFileSync as readFileSync30 } from "node:fs";
28825
- async function ctxExecuteFile(input) {
28765
+ async function ctxExecuteFile(input, executor = new PolyglotExecutor()) {
28826
28766
  const content = readFileSync30(input.path, "utf-8");
28827
28767
  const code = `const FILE_CONTENT = ${JSON.stringify(content)};
28828
28768
  // User script follows:
28829
28769
  `;
28830
- const executor = new PolyglotExecutor();
28831
28770
  return executor.executeFile({
28832
28771
  language: input.language,
28833
28772
  path: input.path,
@@ -28837,9 +28776,8 @@ async function ctxExecuteFile(input) {
28837
28776
  }
28838
28777
 
28839
28778
  // packages/compactor/src/tools/ctx-batch-execute.ts
28840
- async function ctxBatchExecute(items) {
28779
+ async function ctxBatchExecute(items, executor = new PolyglotExecutor()) {
28841
28780
  const results = [];
28842
- const executor = new PolyglotExecutor();
28843
28781
  for (const item of items) {
28844
28782
  if (item.type === "execute") {
28845
28783
  const result = await executor.execute({
@@ -29024,74 +28962,85 @@ ${lines.join("\n\n")}`,
29024
28962
  deprecationLog2("vcc_recall", "session_recall");
29025
28963
  return recallExec(tcId, p);
29026
28964
  } });
29027
- const sandboxExec = async (_toolCallId, params) => {
29028
- try {
29029
- const c = deps.getCounters?.();
29030
- if (c) {
29031
- c.sandboxRuns++;
28965
+ if (deps.sandbox) {
28966
+ const { executor, allowedLanguages } = deps.sandbox;
28967
+ const assertAllowedLanguage = (language) => {
28968
+ if (!allowedLanguages.includes(language)) {
28969
+ throw new Error(`Language "${language}" is disabled by Compactor sandbox settings. Allowed: ${allowedLanguages.join(", ") || "none"}`);
29032
28970
  }
29033
- deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29034
- const result = await ctxExecute(params);
29035
- const parts = [];
29036
- if (result.stdout) parts.push(result.stdout);
29037
- if (result.stderr) parts.push(`[stderr] ${result.stderr}`);
29038
- if (result.timedOut) parts.push("[timed out]");
29039
- if (result.exitCode !== 0) parts.push(`[exit code: ${result.exitCode}]`);
29040
- return textResult2(parts.join("\n") || "(no output)", result);
29041
- } catch (err) {
29042
- return textResult2(`Execution error: ${err}`, { error: true });
29043
- }
29044
- };
29045
- pi.registerTool({ name: "sandbox", label: "Sandbox", description: "Run code in a sandboxed environment. Supports 11 languages. Only stdout enters context.", parameters: SandboxParams, execute: sandboxExec });
29046
- pi.registerTool({ name: "ctx_execute", label: "Sandbox", description: "Run code in sandbox. (DEPRECATED: use sandbox instead)", parameters: CtxExecuteParams, async execute(tcId, p) {
29047
- deprecationLog2("ctx_execute", "sandbox");
29048
- return sandboxExec(tcId, p);
29049
- } });
29050
- const sandboxFileExec = async (_toolCallId, params) => {
29051
- try {
29052
- const c = deps.getCounters?.();
29053
- if (c) {
29054
- c.sandboxRuns++;
28971
+ };
28972
+ const sandboxExec = async (_toolCallId, params) => {
28973
+ try {
28974
+ const c = deps.getCounters?.();
28975
+ if (c) {
28976
+ c.sandboxRuns++;
28977
+ }
28978
+ deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
28979
+ assertAllowedLanguage(params.language);
28980
+ const result = await ctxExecute(params, executor);
28981
+ const parts = [];
28982
+ if (result.stdout) parts.push(result.stdout);
28983
+ if (result.stderr) parts.push(`[stderr] ${result.stderr}`);
28984
+ if (result.timedOut) parts.push("[timed out]");
28985
+ if (result.exitCode !== 0) parts.push(`[exit code: ${result.exitCode}]`);
28986
+ return textResult2(parts.join("\n") || "(no output)", result);
28987
+ } catch (err) {
28988
+ return textResult2(`Execution error: ${err}`, { error: true });
29055
28989
  }
29056
- deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29057
- const result = await ctxExecuteFile(params);
29058
- const parts = [];
29059
- if (result.stdout) parts.push(result.stdout);
29060
- if (result.stderr) parts.push(`[stderr] ${result.stderr}`);
29061
- if (result.timedOut) parts.push("[timed out]");
29062
- return textResult2(parts.join("\n") || "(no output)", result);
29063
- } catch (err) {
29064
- return textResult2(`Execution error: ${err}`, { error: true });
29065
- }
29066
- };
29067
- pi.registerTool({ name: "sandbox_file", label: "Sandbox File", description: "Execute a file in the sandbox. File content is injected as FILE_CONTENT variable.", parameters: SandboxFileParams, execute: sandboxFileExec });
29068
- pi.registerTool({ name: "ctx_execute_file", label: "Sandbox File", description: "Execute file in sandbox. (DEPRECATED: use sandbox_file instead)", parameters: CtxExecuteFileParams, async execute(tcId, p) {
29069
- deprecationLog2("ctx_execute_file", "sandbox_file");
29070
- return sandboxFileExec(tcId, p);
29071
- } });
29072
- const sandboxBatchExec = async (_toolCallId, params) => {
29073
- try {
29074
- const c = deps.getCounters?.();
29075
- if (c) {
29076
- c.sandboxRuns++;
28990
+ };
28991
+ pi.registerTool({ name: "sandbox", label: "Sandbox", description: "Run code in a sandboxed environment. Supports 11 languages. Only stdout enters context.", parameters: SandboxParams, execute: sandboxExec });
28992
+ pi.registerTool({ name: "ctx_execute", label: "Sandbox", description: "Run code in sandbox. (DEPRECATED: use sandbox instead)", parameters: CtxExecuteParams, async execute(tcId, p) {
28993
+ deprecationLog2("ctx_execute", "sandbox");
28994
+ return sandboxExec(tcId, p);
28995
+ } });
28996
+ const sandboxFileExec = async (_toolCallId, params) => {
28997
+ try {
28998
+ const c = deps.getCounters?.();
28999
+ if (c) {
29000
+ c.sandboxRuns++;
29001
+ }
29002
+ deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29003
+ assertAllowedLanguage(params.language);
29004
+ const result = await ctxExecuteFile(params, executor);
29005
+ const parts = [];
29006
+ if (result.stdout) parts.push(result.stdout);
29007
+ if (result.stderr) parts.push(`[stderr] ${result.stderr}`);
29008
+ if (result.timedOut) parts.push("[timed out]");
29009
+ return textResult2(parts.join("\n") || "(no output)", result);
29010
+ } catch (err) {
29011
+ return textResult2(`Execution error: ${err}`, { error: true });
29077
29012
  }
29078
- deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29079
- const result = await ctxBatchExecute(params.items);
29080
- const summaries = result.results.map((r, i) => {
29081
- const s = r.result.stdout?.slice(0, 200) || "(no output)";
29082
- return `[${i}] execute \u2192 ${r.result.exitCode === 0 ? "ok" : "fail"}: ${s}`;
29083
- });
29084
- return textResult2(`Batch results (${result.results.length} items):
29013
+ };
29014
+ pi.registerTool({ name: "sandbox_file", label: "Sandbox File", description: "Execute a file in the sandbox. File content is injected as FILE_CONTENT variable.", parameters: SandboxFileParams, execute: sandboxFileExec });
29015
+ pi.registerTool({ name: "ctx_execute_file", label: "Sandbox File", description: "Execute file in sandbox. (DEPRECATED: use sandbox_file instead)", parameters: CtxExecuteFileParams, async execute(tcId, p) {
29016
+ deprecationLog2("ctx_execute_file", "sandbox_file");
29017
+ return sandboxFileExec(tcId, p);
29018
+ } });
29019
+ const sandboxBatchExec = async (_toolCallId, params) => {
29020
+ try {
29021
+ const c = deps.getCounters?.();
29022
+ if (c) {
29023
+ c.sandboxRuns++;
29024
+ }
29025
+ deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29026
+ for (const item of params.items) assertAllowedLanguage(item.language);
29027
+ const result = await ctxBatchExecute(params.items, executor);
29028
+ const summaries = result.results.map((r, i) => {
29029
+ const s = r.result.stdout?.slice(0, 200) || "(no output)";
29030
+ return `[${i}] execute \u2192 ${r.result.exitCode === 0 ? "ok" : "fail"}: ${s}`;
29031
+ });
29032
+ return textResult2(`Batch results (${result.results.length} items):
29085
29033
  ${summaries.join("\n")}`, result);
29086
- } catch (err) {
29087
- return textResult2(`Batch error: ${err}`, { error: true });
29088
- }
29089
- };
29090
- pi.registerTool({ name: "sandbox_batch", label: "Sandbox Batch", description: "Run multiple code executions atomically as a batch.", parameters: SandboxBatchParams, execute: sandboxBatchExec });
29091
- pi.registerTool({ name: "ctx_batch_execute", label: "Sandbox Batch", description: "Run batch operations. (DEPRECATED: use sandbox_batch instead)", parameters: CtxBatchExecuteParams, async execute(tcId, p) {
29092
- deprecationLog2("ctx_batch_execute", "sandbox_batch");
29093
- return sandboxBatchExec(tcId, p);
29094
- } });
29034
+ } catch (err) {
29035
+ return textResult2(`Batch error: ${err}`, { error: true });
29036
+ }
29037
+ };
29038
+ pi.registerTool({ name: "sandbox_batch", label: "Sandbox Batch", description: "Run multiple code executions atomically as a batch.", parameters: SandboxBatchParams, execute: sandboxBatchExec });
29039
+ pi.registerTool({ name: "ctx_batch_execute", label: "Sandbox Batch", description: "Run batch operations. (DEPRECATED: use sandbox_batch instead)", parameters: CtxBatchExecuteParams, async execute(tcId, p) {
29040
+ deprecationLog2("ctx_batch_execute", "sandbox_batch");
29041
+ return sandboxBatchExec(tcId, p);
29042
+ } });
29043
+ }
29095
29044
  const statsExec = async () => {
29096
29045
  try {
29097
29046
  const result = await ctxStats(deps.sessionDB, deps.getSessionId(), deps.getCounters?.());
@@ -29187,9 +29136,6 @@ function measureResponseBytes(event) {
29187
29136
  function isSandboxTool(name) {
29188
29137
  return name === "bash" || name === "Bash";
29189
29138
  }
29190
- function isIndexTool(_name) {
29191
- return false;
29192
- }
29193
29139
  function compactorExtension(pi) {
29194
29140
  let sessionDB = null;
29195
29141
  let executor = null;
@@ -29207,7 +29153,6 @@ function compactorExtension(pi) {
29207
29153
  const getCounters = () => counters;
29208
29154
  const runtimeStats = {
29209
29155
  bytesReturned: {},
29210
- bytesIndexed: 0,
29211
29156
  bytesSandboxed: 0,
29212
29157
  calls: {},
29213
29158
  sessionStart: Date.now(),
@@ -29225,7 +29170,7 @@ function compactorExtension(pi) {
29225
29170
  } catch {
29226
29171
  sessionDB = null;
29227
29172
  }
29228
- executor = new PolyglotExecutor();
29173
+ executor = null;
29229
29174
  };
29230
29175
  registerCompactionHooks(pi, {
29231
29176
  getSessionDB: () => sessionDB,
@@ -29254,7 +29199,6 @@ function compactorExtension(pi) {
29254
29199
  }
29255
29200
  }
29256
29201
  runtimeStats.bytesReturned = {};
29257
- runtimeStats.bytesIndexed = 0;
29258
29202
  runtimeStats.bytesSandboxed = 0;
29259
29203
  runtimeStats.calls = {};
29260
29204
  runtimeStats.sessionStart = Date.now();
@@ -29263,11 +29207,17 @@ function compactorExtension(pi) {
29263
29207
  autoCompactionState = createAutoCompactionState();
29264
29208
  sessionDB?.ensureSession(fullSessionId, projectDir);
29265
29209
  if (sessionDB) {
29210
+ const sandboxEnabled = config.sandboxExecution.enabled && config.sandboxExecution.mode !== "off";
29211
+ executor = sandboxEnabled ? new PolyglotExecutor({ hardCapBytes: config.sandboxExecution.outputLimit, projectRoot: projectDir }) : null;
29266
29212
  registerCompactorTools(pi, {
29267
29213
  sessionDB,
29268
29214
  getSessionId: () => currentSessionId,
29269
29215
  getBlocks: () => cachedBlocks,
29270
- getCounters
29216
+ getCounters,
29217
+ sandbox: executor ? {
29218
+ executor,
29219
+ allowedLanguages: config.sandboxExecution.allowedLanguages
29220
+ } : void 0
29271
29221
  });
29272
29222
  }
29273
29223
  registerCommands4(pi, getCommandDeps());
@@ -29353,21 +29303,11 @@ function compactorExtension(pi) {
29353
29303
  }
29354
29304
  } catch {
29355
29305
  }
29356
- if (sessionDB) {
29357
- const snapshot = await injectResumeSnapshot(sessionDB, currentSessionId);
29358
- debug("resume_snapshot", { injected: !!snapshot });
29359
- if (snapshot && sessionDB) {
29360
- try {
29361
- const { buildAutoInjection: buildAutoInjection2 } = await Promise.resolve().then(() => (init_auto_inject(), auto_inject_exports));
29362
- const events = sessionDB.getEvents(currentSessionId, { limit: 100 });
29363
- const autoInjection = buildAutoInjection2(events);
29364
- if (autoInjection) {
29365
- debug("auto_injection", { tokens: autoInjection.tokens, length: autoInjection.text.length });
29366
- }
29367
- } catch (err) {
29368
- debug("auto_injection_error", { error: String(err) });
29369
- }
29370
- }
29306
+ const continuityEnabled = isSessionContinuityEnabled(config);
29307
+ if (sessionDB && continuityEnabled) {
29308
+ const resumeContext = await buildResumeContextMessage(sessionDB, currentSessionId);
29309
+ debug("resume_snapshot", { injected: !!resumeContext });
29310
+ return resumeContext;
29371
29311
  }
29372
29312
  });
29373
29313
  pi.on("turn_end", async (_event, ctx) => {
@@ -29423,7 +29363,8 @@ function compactorExtension(pi) {
29423
29363
  }
29424
29364
  });
29425
29365
  pi.on("session_before_compact", async (event, _ctx) => {
29426
- if (sessionDB) {
29366
+ const continuityEnabled = isSessionContinuityEnabled(config);
29367
+ if (sessionDB && continuityEnabled) {
29427
29368
  const sessionId = currentSessionId;
29428
29369
  const events = sessionDB.getEvents(sessionId, { limit: 1e3 });
29429
29370
  const stats = sessionDB.getSessionStats(sessionId);
@@ -29442,12 +29383,18 @@ function compactorExtension(pi) {
29442
29383
  counters.compactions++;
29443
29384
  const compactionEntry = event.compactionEntry;
29444
29385
  const tokensBefore = compactionEntry?.tokensBefore ?? 0;
29386
+ let summarized = 0;
29387
+ let kept = 0;
29388
+ let tokensSaved = 0;
29445
29389
  if (tokensBefore > 0) {
29446
29390
  const charsBefore = tokensBefore * 4;
29447
- const tokensAfter = event.tokensAfter ?? Math.round(tokensBefore * 0.12);
29391
+ const tokensAfter = Math.round(tokensBefore * 0.12);
29392
+ kept = tokensAfter;
29393
+ tokensSaved = tokensBefore - tokensAfter;
29448
29394
  const charsKept = tokensAfter * 4;
29449
29395
  const messagesSummarized = Math.max(1, Math.round(tokensBefore / 500));
29450
- counters.totalTokensCompacted += tokensBefore - tokensAfter;
29396
+ summarized = messagesSummarized;
29397
+ counters.totalTokensCompacted += tokensSaved;
29451
29398
  sessionDB.addCompactionStats(sessionId, charsBefore, charsKept, messagesSummarized);
29452
29399
  } else {
29453
29400
  try {
@@ -29457,12 +29404,23 @@ function compactorExtension(pi) {
29457
29404
  const estTokensAfter = Math.round(estTokensBefore * 0.12);
29458
29405
  const charsBefore = estTokensBefore * 4;
29459
29406
  const charsKept = estTokensAfter * 4;
29460
- counters.totalTokensCompacted += estTokensBefore - estTokensAfter;
29407
+ summarized = eventCount;
29408
+ kept = estTokensAfter;
29409
+ tokensSaved = estTokensBefore - estTokensAfter;
29410
+ counters.totalTokensCompacted += tokensSaved;
29461
29411
  sessionDB.addCompactionStats(sessionId, charsBefore, charsKept, eventCount);
29462
29412
  }
29463
29413
  } catch {
29464
29414
  }
29465
29415
  }
29416
+ const totalEstimated = tokensSaved + kept;
29417
+ emitEvent(pi, UNIPI_EVENTS.COMPACTOR_COMPACTED, {
29418
+ sessionId,
29419
+ summarized,
29420
+ kept,
29421
+ tokensSaved,
29422
+ compressionRatio: kept > 0 ? `${Math.round(totalEstimated / kept)}:1` : "0:1"
29423
+ });
29466
29424
  debug("session_compact", { sessionId, tokensBefore, hasCompactionEntry: !!compactionEntry });
29467
29425
  }
29468
29426
  });
@@ -29554,30 +29512,10 @@ function compactorExtension(pi) {
29554
29512
  if (isSandboxTool(tName)) {
29555
29513
  runtimeStats.bytesSandboxed += responseBytes;
29556
29514
  }
29557
- if (isIndexTool(tName)) {
29558
- runtimeStats.bytesIndexed += responseBytes;
29559
- }
29560
29515
  }
29561
29516
  } catch {
29562
29517
  }
29563
29518
  const toolName = event.toolName ?? "";
29564
- const td = config.toolDisplay;
29565
- const toolConfig = {
29566
- readOutputMode: td?.mode,
29567
- searchOutputMode: td?.mode,
29568
- bashOutputMode: td?.mode,
29569
- previewLines: 20,
29570
- bashCollapsedLines: 5,
29571
- showTruncationHints: true
29572
- };
29573
- try {
29574
- const { applyToolDisplayOverride: applyToolDisplayOverride2 } = await Promise.resolve().then(() => (init_tool_overrides(), tool_overrides_exports));
29575
- const override = applyToolDisplayOverride2(toolName, event, toolConfig);
29576
- if (override !== void 0) {
29577
- return override;
29578
- }
29579
- } catch {
29580
- }
29581
29519
  const diffToolNames = ["edit", "Edit", "write", "Write"];
29582
29520
  if (diffToolNames.includes(toolName)) {
29583
29521
  try {
@@ -30886,14 +30824,6 @@ var FooterRenderer = class _FooterRenderer {
30886
30824
  init_core();
30887
30825
  function subscribeToEvents(pi, registry3) {
30888
30826
  const unsubscribers = [];
30889
- unsubscribers.push(
30890
- pi.events.on(UNIPI_EVENTS.COMPACTOR_STATS_UPDATED, (event) => {
30891
- try {
30892
- registry3.updateData("compactor", event);
30893
- } catch {
30894
- }
30895
- })
30896
- );
30897
30827
  unsubscribers.push(
30898
30828
  pi.events.on(UNIPI_EVENTS.COMPACTOR_COMPACTED, (event) => {
30899
30829
  try {