@pi-unipi/unipi 2.4.0 → 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 (99) hide show
  1. package/CHANGELOG.md +43 -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/auto-trigger.ts +0 -0
  11. package/packages/compactor/src/compaction/build-sections.ts +2 -2
  12. package/packages/compactor/src/compaction/format.ts +1 -1
  13. package/packages/compactor/src/config/presets.ts +19 -30
  14. package/packages/compactor/src/config/schema.ts +0 -0
  15. package/packages/compactor/src/index.ts +39 -56
  16. package/packages/compactor/src/session/resume-inject.ts +27 -0
  17. package/packages/compactor/src/tools/ctx-batch-execute.ts +4 -2
  18. package/packages/compactor/src/tools/ctx-execute-file.ts +4 -2
  19. package/packages/compactor/src/tools/ctx-execute.ts +4 -2
  20. package/packages/compactor/src/tools/register.ts +22 -4
  21. package/packages/compactor/src/tui/settings-overlay.ts +9 -22
  22. package/packages/compactor/src/types.ts +24 -2
  23. package/packages/core/events.ts +13 -13
  24. package/packages/core/package.json +3 -2
  25. package/packages/footer/README.md +2 -2
  26. package/packages/footer/package.json +3 -3
  27. package/packages/footer/src/events.ts +0 -10
  28. package/packages/footer/src/segments/core.ts +0 -9
  29. package/packages/image/package.json +2 -2
  30. package/packages/info-screen/core-groups.ts +0 -14
  31. package/packages/info-screen/package.json +2 -2
  32. package/packages/input-shortcuts/package.json +2 -2
  33. package/packages/kanboard/commands.ts +0 -1
  34. package/packages/kanboard/package.json +2 -2
  35. package/packages/mcp/package.json +2 -2
  36. package/packages/memory/package.json +3 -3
  37. package/packages/memory/storage.ts +0 -18
  38. package/packages/memory/tools.ts +0 -1
  39. package/packages/milestone/hooks.ts +9 -15
  40. package/packages/milestone/package.json +2 -2
  41. package/packages/notify/README.md +2 -0
  42. package/packages/notify/events.ts +23 -13
  43. package/packages/notify/package.json +2 -2
  44. package/packages/notify/tools.ts +17 -8
  45. package/packages/notify/types.ts +5 -1
  46. package/packages/ralph/index.ts +10 -5
  47. package/packages/ralph/package.json +3 -3
  48. package/packages/ralph/ralph-loop.ts +10 -0
  49. package/packages/subagents/README.md +3 -1
  50. package/packages/subagents/package.json +10 -6
  51. package/packages/subagents/src/agent-manager.ts +46 -11
  52. package/packages/subagents/src/core-compat.ts +38 -0
  53. package/packages/subagents/src/global.d.ts +7 -0
  54. package/packages/subagents/src/index.ts +6 -3
  55. package/packages/subagents/src/widget.ts +0 -7
  56. package/packages/unipi/bundled.js +371 -438
  57. package/packages/updater/package.json +4 -3
  58. package/packages/utility/README.md +1 -1
  59. package/packages/utility/package.json +7 -4
  60. package/packages/utility/src/commands.ts +2 -10
  61. package/packages/utility/src/diff/theme.ts +0 -3
  62. package/packages/utility/src/tui/name-badge.ts +0 -9
  63. package/packages/web-api/package.json +2 -2
  64. package/packages/web-api/src/engine/extract.ts +0 -26
  65. package/packages/web-api/src/tools.ts +0 -13
  66. package/packages/workflow/commands.ts +13 -1
  67. package/packages/workflow/index.ts +12 -1
  68. package/packages/workflow/lifecycle.ts +43 -0
  69. package/packages/workflow/package.json +3 -2
  70. package/packages/autocomplete/index.ts +0 -7
  71. package/packages/autocomplete/src/__tests__/command-registry.audit.test.ts +0 -120
  72. package/packages/autocomplete/src/__tests__/provider.sorting.test.ts +0 -423
  73. package/packages/compactor/src/compaction/recall-scope.ts +0 -32
  74. package/packages/compactor/src/compaction/sections.ts +0 -15
  75. package/packages/compactor/src/display/bash-display.ts +0 -28
  76. package/packages/compactor/src/display/diff-presentation.ts +0 -25
  77. package/packages/compactor/src/display/pending-diff-preview.ts +0 -51
  78. package/packages/compactor/src/display/user-message-box.ts +0 -16
  79. package/packages/compactor/src/session/analytics.ts +0 -205
  80. package/packages/image/index.ts +0 -1
  81. package/packages/input-shortcuts/index.ts +0 -5
  82. package/packages/input-shortcuts/src/status.ts +0 -35
  83. package/packages/kanboard/tui/kanboard-overlay.ts +0 -300
  84. package/packages/mcp/index.ts +0 -1
  85. package/packages/memory/search.ts +0 -134
  86. package/packages/milestone/coexist.ts +0 -114
  87. package/packages/notify/src/__tests__/ask-user-prompt-message.test.ts +0 -159
  88. package/packages/notify/src/__tests__/event-bus.test.ts +0 -119
  89. package/packages/notify/src/__tests__/permission-prompt-message.test.ts +0 -172
  90. package/packages/subagents/src/__tests__/badge-generation.test.ts +0 -315
  91. package/packages/subagents/src/__tests__/config.test.ts +0 -240
  92. package/packages/subagents/src/__tests__/esc-propagation.test.ts +0 -162
  93. package/packages/subagents/src/__tests__/file-lock.test.ts +0 -244
  94. package/packages/subagents/src/__tests__/shutdown-stale-ctx.test.ts +0 -185
  95. package/packages/subagents/src/__tests__/workflow-integration.test.ts +0 -334
  96. package/packages/subagents/src/prompts.ts +0 -39
  97. package/packages/utility/index.ts +0 -1
  98. package/packages/web-api/src/tui/progress.ts +0 -168
  99. 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;
@@ -7743,7 +7581,7 @@ function ralph_default(pi) {
7743
7581
  );
7744
7582
  }
7745
7583
  });
7746
- pi.on("before_agent_start", async (event, ctx) => {
7584
+ pi.on("before_agent_start", async (_event, ctx) => {
7747
7585
  const mgr = getManager(ctx, pi);
7748
7586
  const currentLoop = mgr.getCurrentLoop();
7749
7587
  if (!currentLoop) return;
@@ -7762,10 +7600,13 @@ function ralph_default(pi) {
7762
7600
  `;
7763
7601
  instructions += `- Otherwise, call ralph_done tool to proceed to next iteration`;
7764
7602
  return {
7765
- systemPrompt: event.systemPrompt + `
7766
- [RALPH LOOP - ${state.name} - Iteration ${iterStr}]
7603
+ message: {
7604
+ customType: "unipi-ralph-loop-reminder",
7605
+ content: `[RALPH LOOP - ${state.name} - Iteration ${iterStr}]
7767
7606
 
7768
- ${instructions}`
7607
+ ${instructions}`,
7608
+ display: false
7609
+ }
7769
7610
  };
7770
7611
  });
7771
7612
  pi.on("session_shutdown", async (_event, ctx) => {
@@ -10732,7 +10573,6 @@ function info_screen_default(pi) {
10732
10573
  }
10733
10574
 
10734
10575
  // packages/subagents/src/index.ts
10735
- init_core();
10736
10576
  import { defineTool } from "@earendil-works/pi-coding-agent";
10737
10577
  import { Text } from "@earendil-works/pi-tui";
10738
10578
  import { Type as Type3 } from "typebox";
@@ -10740,6 +10580,29 @@ import { existsSync as existsSync13, readdirSync as readdirSync7 } from "node:fs
10740
10580
  import { join as join13 } from "node:path";
10741
10581
  import { homedir as homedir9 } from "node:os";
10742
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
+
10743
10606
  // packages/subagents/src/agent-manager.ts
10744
10607
  import { randomUUID } from "node:crypto";
10745
10608
 
@@ -11121,23 +10984,43 @@ var AgentManager = class {
11121
10984
  onStart;
11122
10985
  maxConcurrent;
11123
10986
  customAgents;
10987
+ typeSettings;
11124
10988
  /** Per-file transparent locking for write agents. */
11125
10989
  fileLock = new FileLock();
11126
10990
  /** Queue of background agents waiting to start. */
11127
10991
  queue = [];
11128
10992
  /** Number of currently running background agents. */
11129
10993
  runningBackground = 0;
11130
- constructor(onComplete, maxConcurrent = DEFAULT_MAX_CONCURRENT, onStart) {
10994
+ constructor(onComplete, maxConcurrent = DEFAULT_MAX_CONCURRENT, onStart, typeSettings = {}, agentsCwd = process.cwd()) {
11131
10995
  this.onComplete = onComplete;
11132
10996
  this.onStart = onStart;
11133
10997
  this.maxConcurrent = maxConcurrent;
11134
- this.customAgents = loadCustomAgents(process.cwd());
10998
+ this.typeSettings = typeSettings;
10999
+ this.customAgents = loadCustomAgents(agentsCwd);
11135
11000
  this.cleanupInterval = setInterval(() => this.cleanup(), 6e4);
11136
11001
  }
11137
11002
  /** Get resolved agent config for a type. */
11138
11003
  getAgentConfig(type) {
11139
11004
  return this.customAgents.get(type) ?? BUILTIN_CONFIGS[type];
11140
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
+ }
11141
11024
  setMaxConcurrent(n) {
11142
11025
  this.maxConcurrent = Math.max(1, n);
11143
11026
  this.drainQueue();
@@ -11149,6 +11032,7 @@ var AgentManager = class {
11149
11032
  * Spawn an agent. Returns ID immediately for background, waits for foreground.
11150
11033
  */
11151
11034
  spawn(pi, ctx, type, prompt, options) {
11035
+ this.assertTypeEnabled(type);
11152
11036
  const id = randomUUID().slice(0, 17);
11153
11037
  const abortController = new AbortController();
11154
11038
  const record = {
@@ -11176,9 +11060,11 @@ var AgentManager = class {
11176
11060
  record.startedAt = Date.now();
11177
11061
  if (options.isBackground) this.runningBackground++;
11178
11062
  this.onStart?.(record);
11063
+ const agentConfig = this.getAgentConfig(type);
11179
11064
  let model = options.model;
11180
- if (options.modelInput && options.modelRegistry) {
11181
- 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);
11182
11068
  if (typeof resolved === "string") {
11183
11069
  record.status = "error";
11184
11070
  record.error = resolved;
@@ -11191,15 +11077,14 @@ var AgentManager = class {
11191
11077
  }
11192
11078
  model = resolved;
11193
11079
  }
11194
- const agentConfig = this.getAgentConfig(type);
11195
11080
  const promise = runAgent(ctx, type, prompt, {
11196
11081
  pi,
11197
11082
  model,
11198
11083
  agentConfig,
11199
- maxTurns: options.maxTurns,
11200
- isolated: options.isolated,
11201
- inheritContext: options.inheritContext,
11202
- 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,
11203
11088
  signal: record.abortController.signal,
11204
11089
  onToolActivity: (activity) => {
11205
11090
  if (activity.type === "end") record.toolUses++;
@@ -12139,7 +12024,8 @@ function src_default(pi) {
12139
12024
  type: record.type,
12140
12025
  description: record.description
12141
12026
  });
12142
- }
12027
+ },
12028
+ config.types
12143
12029
  );
12144
12030
  function buildNotificationDetails(record, activity) {
12145
12031
  return {
@@ -12242,14 +12128,14 @@ function src_default(pi) {
12242
12128
  let sessionCtx2 = null;
12243
12129
  pi.on("session_start", async (_event, ctx) => {
12244
12130
  sessionCtx2 = ctx;
12245
- emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
12246
- name: MODULES.SUBAGENTS || "subagents",
12131
+ emitEvent2(pi, UNIPI_EVENTS2.MODULE_READY, {
12132
+ name: MODULES2.SUBAGENTS || "subagents",
12247
12133
  version: "0.2.0",
12248
12134
  commands: [],
12249
12135
  tools: ["spawn_helper", "get_helper_result"]
12250
12136
  });
12251
12137
  });
12252
- pi.events.on(UNIPI_EVENTS.BADGE_GENERATE_REQUEST, async (data) => {
12138
+ pi.events.on(UNIPI_EVENTS2.BADGE_GENERATE_REQUEST, async (data) => {
12253
12139
  const event = data;
12254
12140
  if (!sessionCtx2) return;
12255
12141
  const summary = event?.conversationSummary ?? "";
@@ -12335,13 +12221,15 @@ ${summary}` : `Generate a concise session title (MAX 5 WORDS) for this session.
12335
12221
  return { state, callbacks };
12336
12222
  }
12337
12223
  const builtinTypes = BUILTIN_TYPES.join(", ");
12224
+ const enabledTypes = manager2.getKnownTypes().filter((type) => manager2.isTypeEnabled(type));
12225
+ const availableTypes = enabledTypes.join(", ") || "none";
12338
12226
  pi.registerTool(
12339
12227
  defineTool({
12340
12228
  name: "spawn_helper",
12341
12229
  label: "Spawn Helper",
12342
12230
  description: `Launch a sub-agent for parallel work.
12343
12231
 
12344
- Available agent types: ${builtinTypes}
12232
+ Available agent types: ${availableTypes}
12345
12233
  Custom types can be defined in:
12346
12234
  - ~/.unipi/config/agents/<name>.md (global)
12347
12235
  - <workspace>/.unipi/config/agents/<name>.md (project)
@@ -12354,7 +12242,7 @@ Guidelines:
12354
12242
  - Agents inherit the parent model by default`,
12355
12243
  parameters: Type3.Object({
12356
12244
  type: Type3.String({
12357
- description: `Agent type: ${builtinTypes}, or custom type from ~/.unipi/config/agents/*.md`
12245
+ description: `Enabled agent type: ${availableTypes}`
12358
12246
  }),
12359
12247
  prompt: Type3.String({
12360
12248
  description: "The task for the agent to perform."
@@ -12597,7 +12485,7 @@ Use get_result to retrieve full results.`,
12597
12485
  }
12598
12486
  if (params.view && record.session) {
12599
12487
  const activity = agentActivity.get(record.id);
12600
- await withHerdrBlocked(
12488
+ await withHerdrBlocked2(
12601
12489
  pi,
12602
12490
  "helper viewer",
12603
12491
  () => ctx.ui.custom(
@@ -17584,7 +17472,6 @@ var PRESETS = {
17584
17472
  }
17585
17473
  }
17586
17474
  };
17587
- var HARDCODED_FALLBACK = PRESETS.default.colors;
17588
17475
  function getPreset(name) {
17589
17476
  return PRESETS[name] ?? PRESETS.default;
17590
17477
  }
@@ -18239,17 +18126,9 @@ function registerUtilityCommands(pi) {
18239
18126
  }
18240
18127
  return;
18241
18128
  }
18242
- const requestId = `status-${Date.now()}`;
18243
- emitEvent(pi, UNIPI_EVENTS.MODULE_STATUS_REQUEST, { requestId });
18244
- await new Promise((resolve13) => setTimeout(resolve13, 500));
18245
18129
  sendResponse(
18246
18130
  pi,
18247
- `## \u{1F4CA} Module Status
18248
-
18249
- Status request broadcast to all modules.
18250
- Modules that support status reporting will respond via events.
18251
-
18252
- *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."
18253
18132
  );
18254
18133
  }
18255
18134
  });
@@ -23728,7 +23607,7 @@ function getEnabledPlatforms(config, ntfyEnabled) {
23728
23607
  function unregisterEventListeners() {
23729
23608
  unregisterAll();
23730
23609
  }
23731
- async function dispatchNotification(pi, title, message, eventPlatforms, eventType, config, cwd) {
23610
+ async function dispatchNotification(pi, title, message, eventPlatforms, eventType, config, cwd, priority) {
23732
23611
  const ntfyConfig = loadNtfyConfig(cwd);
23733
23612
  const platforms = eventPlatforms.length > 0 ? eventPlatforms : getEnabledPlatforms(config, ntfyConfig.enabled).length > 0 ? getEnabledPlatforms(config, ntfyConfig.enabled) : config.defaultPlatforms;
23734
23613
  const enabledPlatforms = platforms.filter((p) => {
@@ -23741,8 +23620,8 @@ async function dispatchNotification(pi, title, message, eventPlatforms, eventTyp
23741
23620
  const results = await Promise.all(
23742
23621
  enabledPlatforms.map(async (platform) => {
23743
23622
  try {
23744
- await sendToPlatform(platform, title, message, config, cwd);
23745
- 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 } };
23746
23625
  } catch (err) {
23747
23626
  if (err instanceof SuppressedError2) {
23748
23627
  return { platform, success: true, suppressed: true };
@@ -23767,14 +23646,21 @@ async function dispatchNotification(pi, title, message, eventPlatforms, eventTyp
23767
23646
  });
23768
23647
  return { results, allSuccess };
23769
23648
  }
23770
- 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) {
23771
23657
  switch (platform) {
23772
23658
  case "native":
23773
23659
  await sendNativeNotification(title, message, {
23774
23660
  windowsAppId: config.native.windowsAppId,
23775
23661
  suppressWhenFocused: config.native.suppressWhenFocused
23776
23662
  });
23777
- break;
23663
+ return void 0;
23778
23664
  case "gotify":
23779
23665
  if (!config.gotify.serverUrl || !config.gotify.appToken) {
23780
23666
  throw new Error("Gotify: serverUrl and appToken are required");
@@ -23784,9 +23670,9 @@ async function sendToPlatform(platform, title, message, config, cwd) {
23784
23670
  config.gotify.appToken,
23785
23671
  title,
23786
23672
  message,
23787
- config.gotify.priority
23673
+ priority ? mapNotifyPriority("gotify", priority) : config.gotify.priority
23788
23674
  );
23789
- break;
23675
+ return priority ? mapNotifyPriority("gotify", priority) : config.gotify.priority;
23790
23676
  case "telegram":
23791
23677
  if (!config.telegram.botToken || !config.telegram.chatId) {
23792
23678
  throw new Error("Telegram: botToken and chatId are required");
@@ -23797,10 +23683,10 @@ async function sendToPlatform(platform, title, message, config, cwd) {
23797
23683
  title,
23798
23684
  message
23799
23685
  );
23800
- break;
23686
+ return void 0;
23801
23687
  case "ntfy": {
23802
23688
  const ntfyConfig = loadNtfyConfig(cwd);
23803
- if (!ntfyConfig.enabled) return;
23689
+ if (!ntfyConfig.enabled) return void 0;
23804
23690
  if (!ntfyConfig.serverUrl || !ntfyConfig.topic) {
23805
23691
  throw new Error("ntfy: serverUrl and topic are required");
23806
23692
  }
@@ -23809,10 +23695,10 @@ async function sendToPlatform(platform, title, message, config, cwd) {
23809
23695
  ntfyConfig.topic,
23810
23696
  title,
23811
23697
  message,
23812
- ntfyConfig.priority,
23698
+ priority ? mapNotifyPriority("ntfy", priority) : ntfyConfig.priority,
23813
23699
  ntfyConfig.token
23814
23700
  );
23815
- break;
23701
+ return priority ? mapNotifyPriority("ntfy", priority) : ntfyConfig.priority;
23816
23702
  }
23817
23703
  }
23818
23704
  }
@@ -23941,7 +23827,6 @@ var NotifyUserSchema = Type6.Object({
23941
23827
  priority: Type6.Optional(
23942
23828
  Type6.String({
23943
23829
  enum: ["low", "normal", "high"],
23944
- default: "normal",
23945
23830
  description: "Priority level"
23946
23831
  })
23947
23832
  ),
@@ -23962,23 +23847,31 @@ function registerNotifyTools(pi) {
23962
23847
  const {
23963
23848
  message,
23964
23849
  title,
23965
- priority: _priority,
23850
+ priority,
23966
23851
  platforms
23967
23852
  } = params;
23968
23853
  const config = loadConfig2();
23969
23854
  const notifTitle = title || "Pi Notification";
23970
23855
  const notifPlatforms = platforms || config.defaultPlatforms;
23971
23856
  const cwd = process.cwd();
23972
- dispatchNotification(
23857
+ const dispatchPromise = dispatchNotification(
23973
23858
  pi,
23974
23859
  notifTitle,
23975
23860
  message,
23976
23861
  notifPlatforms,
23977
23862
  "agent_tool",
23978
23863
  config,
23979
- cwd
23980
- ).catch(() => {
23864
+ cwd,
23865
+ priority
23866
+ );
23867
+ void dispatchPromise.catch(() => {
23981
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;
23982
23875
  return {
23983
23876
  content: [
23984
23877
  {
@@ -23986,7 +23879,7 @@ function registerNotifyTools(pi) {
23986
23879
  text: `Notification sending to ${notifPlatforms.length} platform(s): ${notifPlatforms.join(", ")}`
23987
23880
  }
23988
23881
  ],
23989
- details: { platforms: notifPlatforms }
23882
+ details: { platforms: notifPlatforms, priority, priorityByPlatform }
23990
23883
  };
23991
23884
  }
23992
23885
  });
@@ -26385,10 +26278,7 @@ function registerSessionEndHook(pi) {
26385
26278
  }
26386
26279
  }
26387
26280
  });
26388
- pi.on("input", (event) => {
26389
- return void 0;
26390
- });
26391
- pi.on("session_shutdown", () => {
26281
+ const syncModifiedDocs = () => {
26392
26282
  const cwd = process.cwd();
26393
26283
  const milestonesPath = path13.join(cwd, MILESTONE_DIRS.MILESTONES);
26394
26284
  if (!fs14.existsSync(milestonesPath)) return;
@@ -26406,7 +26296,9 @@ function registerSessionEndHook(pi) {
26406
26296
  updateItemStatus(milestonesPath, phase, text, true);
26407
26297
  }
26408
26298
  }
26409
- });
26299
+ };
26300
+ pi.events.on(UNIPI_EVENTS.WORKFLOW_END, syncModifiedDocs);
26301
+ pi.on("session_shutdown", syncModifiedDocs);
26410
26302
  }
26411
26303
 
26412
26304
  // packages/milestone/commands.ts
@@ -27976,8 +27868,49 @@ function extractEventsFromToolResult(result) {
27976
27868
 
27977
27869
  // packages/compactor/src/session/resume-inject.ts
27978
27870
  init_snapshot();
27979
- 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
27980
27910
  init_manager();
27911
+ function isSessionContinuityEnabled(config) {
27912
+ return config.sessionContinuity.enabled && config.sessionContinuity.mode !== "off";
27913
+ }
27981
27914
  async function injectResumeSnapshot(db, sessionId, opts) {
27982
27915
  const resume = db.getResume(sessionId);
27983
27916
  if (!resume || resume.consumed) return null;
@@ -28000,6 +27933,17 @@ ${autoInjection}`;
28000
27933
  db.markResumeConsumed(sessionId);
28001
27934
  return fullSnapshot;
28002
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
+ }
28003
27947
 
28004
27948
  // packages/compactor/src/executor/executor.ts
28005
27949
  import { spawn as spawn3, execSync, execFileSync as execFileSync2 } from "node:child_process";
@@ -28808,8 +28752,7 @@ function compactTool() {
28808
28752
  }
28809
28753
 
28810
28754
  // packages/compactor/src/tools/ctx-execute.ts
28811
- async function ctxExecute(input) {
28812
- const executor = new PolyglotExecutor();
28755
+ async function ctxExecute(input, executor = new PolyglotExecutor()) {
28813
28756
  return executor.execute({
28814
28757
  language: input.language,
28815
28758
  code: input.code,
@@ -28819,12 +28762,11 @@ async function ctxExecute(input) {
28819
28762
 
28820
28763
  // packages/compactor/src/tools/ctx-execute-file.ts
28821
28764
  import { readFileSync as readFileSync30 } from "node:fs";
28822
- async function ctxExecuteFile(input) {
28765
+ async function ctxExecuteFile(input, executor = new PolyglotExecutor()) {
28823
28766
  const content = readFileSync30(input.path, "utf-8");
28824
28767
  const code = `const FILE_CONTENT = ${JSON.stringify(content)};
28825
28768
  // User script follows:
28826
28769
  `;
28827
- const executor = new PolyglotExecutor();
28828
28770
  return executor.executeFile({
28829
28771
  language: input.language,
28830
28772
  path: input.path,
@@ -28834,9 +28776,8 @@ async function ctxExecuteFile(input) {
28834
28776
  }
28835
28777
 
28836
28778
  // packages/compactor/src/tools/ctx-batch-execute.ts
28837
- async function ctxBatchExecute(items) {
28779
+ async function ctxBatchExecute(items, executor = new PolyglotExecutor()) {
28838
28780
  const results = [];
28839
- const executor = new PolyglotExecutor();
28840
28781
  for (const item of items) {
28841
28782
  if (item.type === "execute") {
28842
28783
  const result = await executor.execute({
@@ -29021,74 +28962,85 @@ ${lines.join("\n\n")}`,
29021
28962
  deprecationLog2("vcc_recall", "session_recall");
29022
28963
  return recallExec(tcId, p);
29023
28964
  } });
29024
- const sandboxExec = async (_toolCallId, params) => {
29025
- try {
29026
- const c = deps.getCounters?.();
29027
- if (c) {
29028
- 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"}`);
29029
28970
  }
29030
- deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29031
- const result = await ctxExecute(params);
29032
- const parts = [];
29033
- if (result.stdout) parts.push(result.stdout);
29034
- if (result.stderr) parts.push(`[stderr] ${result.stderr}`);
29035
- if (result.timedOut) parts.push("[timed out]");
29036
- if (result.exitCode !== 0) parts.push(`[exit code: ${result.exitCode}]`);
29037
- return textResult2(parts.join("\n") || "(no output)", result);
29038
- } catch (err) {
29039
- return textResult2(`Execution error: ${err}`, { error: true });
29040
- }
29041
- };
29042
- pi.registerTool({ name: "sandbox", label: "Sandbox", description: "Run code in a sandboxed environment. Supports 11 languages. Only stdout enters context.", parameters: SandboxParams, execute: sandboxExec });
29043
- pi.registerTool({ name: "ctx_execute", label: "Sandbox", description: "Run code in sandbox. (DEPRECATED: use sandbox instead)", parameters: CtxExecuteParams, async execute(tcId, p) {
29044
- deprecationLog2("ctx_execute", "sandbox");
29045
- return sandboxExec(tcId, p);
29046
- } });
29047
- const sandboxFileExec = async (_toolCallId, params) => {
29048
- try {
29049
- const c = deps.getCounters?.();
29050
- if (c) {
29051
- 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 });
29052
28989
  }
29053
- deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29054
- const result = await ctxExecuteFile(params);
29055
- const parts = [];
29056
- if (result.stdout) parts.push(result.stdout);
29057
- if (result.stderr) parts.push(`[stderr] ${result.stderr}`);
29058
- if (result.timedOut) parts.push("[timed out]");
29059
- return textResult2(parts.join("\n") || "(no output)", result);
29060
- } catch (err) {
29061
- return textResult2(`Execution error: ${err}`, { error: true });
29062
- }
29063
- };
29064
- 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 });
29065
- 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) {
29066
- deprecationLog2("ctx_execute_file", "sandbox_file");
29067
- return sandboxFileExec(tcId, p);
29068
- } });
29069
- const sandboxBatchExec = async (_toolCallId, params) => {
29070
- try {
29071
- const c = deps.getCounters?.();
29072
- if (c) {
29073
- 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 });
29074
29012
  }
29075
- deps.sessionDB.incrementSandboxRuns(deps.getSessionId());
29076
- const result = await ctxBatchExecute(params.items);
29077
- const summaries = result.results.map((r, i) => {
29078
- const s = r.result.stdout?.slice(0, 200) || "(no output)";
29079
- return `[${i}] execute \u2192 ${r.result.exitCode === 0 ? "ok" : "fail"}: ${s}`;
29080
- });
29081
- 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):
29082
29033
  ${summaries.join("\n")}`, result);
29083
- } catch (err) {
29084
- return textResult2(`Batch error: ${err}`, { error: true });
29085
- }
29086
- };
29087
- pi.registerTool({ name: "sandbox_batch", label: "Sandbox Batch", description: "Run multiple code executions atomically as a batch.", parameters: SandboxBatchParams, execute: sandboxBatchExec });
29088
- pi.registerTool({ name: "ctx_batch_execute", label: "Sandbox Batch", description: "Run batch operations. (DEPRECATED: use sandbox_batch instead)", parameters: CtxBatchExecuteParams, async execute(tcId, p) {
29089
- deprecationLog2("ctx_batch_execute", "sandbox_batch");
29090
- return sandboxBatchExec(tcId, p);
29091
- } });
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
+ }
29092
29044
  const statsExec = async () => {
29093
29045
  try {
29094
29046
  const result = await ctxStats(deps.sessionDB, deps.getSessionId(), deps.getCounters?.());
@@ -29184,9 +29136,6 @@ function measureResponseBytes(event) {
29184
29136
  function isSandboxTool(name) {
29185
29137
  return name === "bash" || name === "Bash";
29186
29138
  }
29187
- function isIndexTool(_name) {
29188
- return false;
29189
- }
29190
29139
  function compactorExtension(pi) {
29191
29140
  let sessionDB = null;
29192
29141
  let executor = null;
@@ -29204,7 +29153,6 @@ function compactorExtension(pi) {
29204
29153
  const getCounters = () => counters;
29205
29154
  const runtimeStats = {
29206
29155
  bytesReturned: {},
29207
- bytesIndexed: 0,
29208
29156
  bytesSandboxed: 0,
29209
29157
  calls: {},
29210
29158
  sessionStart: Date.now(),
@@ -29222,7 +29170,7 @@ function compactorExtension(pi) {
29222
29170
  } catch {
29223
29171
  sessionDB = null;
29224
29172
  }
29225
- executor = new PolyglotExecutor();
29173
+ executor = null;
29226
29174
  };
29227
29175
  registerCompactionHooks(pi, {
29228
29176
  getSessionDB: () => sessionDB,
@@ -29251,7 +29199,6 @@ function compactorExtension(pi) {
29251
29199
  }
29252
29200
  }
29253
29201
  runtimeStats.bytesReturned = {};
29254
- runtimeStats.bytesIndexed = 0;
29255
29202
  runtimeStats.bytesSandboxed = 0;
29256
29203
  runtimeStats.calls = {};
29257
29204
  runtimeStats.sessionStart = Date.now();
@@ -29260,11 +29207,17 @@ function compactorExtension(pi) {
29260
29207
  autoCompactionState = createAutoCompactionState();
29261
29208
  sessionDB?.ensureSession(fullSessionId, projectDir);
29262
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;
29263
29212
  registerCompactorTools(pi, {
29264
29213
  sessionDB,
29265
29214
  getSessionId: () => currentSessionId,
29266
29215
  getBlocks: () => cachedBlocks,
29267
- getCounters
29216
+ getCounters,
29217
+ sandbox: executor ? {
29218
+ executor,
29219
+ allowedLanguages: config.sandboxExecution.allowedLanguages
29220
+ } : void 0
29268
29221
  });
29269
29222
  }
29270
29223
  registerCommands4(pi, getCommandDeps());
@@ -29350,21 +29303,11 @@ function compactorExtension(pi) {
29350
29303
  }
29351
29304
  } catch {
29352
29305
  }
29353
- if (sessionDB) {
29354
- const snapshot = await injectResumeSnapshot(sessionDB, currentSessionId);
29355
- debug("resume_snapshot", { injected: !!snapshot });
29356
- if (snapshot && sessionDB) {
29357
- try {
29358
- const { buildAutoInjection: buildAutoInjection2 } = await Promise.resolve().then(() => (init_auto_inject(), auto_inject_exports));
29359
- const events = sessionDB.getEvents(currentSessionId, { limit: 100 });
29360
- const autoInjection = buildAutoInjection2(events);
29361
- if (autoInjection) {
29362
- debug("auto_injection", { tokens: autoInjection.tokens, length: autoInjection.text.length });
29363
- }
29364
- } catch (err) {
29365
- debug("auto_injection_error", { error: String(err) });
29366
- }
29367
- }
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;
29368
29311
  }
29369
29312
  });
29370
29313
  pi.on("turn_end", async (_event, ctx) => {
@@ -29420,7 +29363,8 @@ function compactorExtension(pi) {
29420
29363
  }
29421
29364
  });
29422
29365
  pi.on("session_before_compact", async (event, _ctx) => {
29423
- if (sessionDB) {
29366
+ const continuityEnabled = isSessionContinuityEnabled(config);
29367
+ if (sessionDB && continuityEnabled) {
29424
29368
  const sessionId = currentSessionId;
29425
29369
  const events = sessionDB.getEvents(sessionId, { limit: 1e3 });
29426
29370
  const stats = sessionDB.getSessionStats(sessionId);
@@ -29439,12 +29383,18 @@ function compactorExtension(pi) {
29439
29383
  counters.compactions++;
29440
29384
  const compactionEntry = event.compactionEntry;
29441
29385
  const tokensBefore = compactionEntry?.tokensBefore ?? 0;
29386
+ let summarized = 0;
29387
+ let kept = 0;
29388
+ let tokensSaved = 0;
29442
29389
  if (tokensBefore > 0) {
29443
29390
  const charsBefore = tokensBefore * 4;
29444
- const tokensAfter = event.tokensAfter ?? Math.round(tokensBefore * 0.12);
29391
+ const tokensAfter = Math.round(tokensBefore * 0.12);
29392
+ kept = tokensAfter;
29393
+ tokensSaved = tokensBefore - tokensAfter;
29445
29394
  const charsKept = tokensAfter * 4;
29446
29395
  const messagesSummarized = Math.max(1, Math.round(tokensBefore / 500));
29447
- counters.totalTokensCompacted += tokensBefore - tokensAfter;
29396
+ summarized = messagesSummarized;
29397
+ counters.totalTokensCompacted += tokensSaved;
29448
29398
  sessionDB.addCompactionStats(sessionId, charsBefore, charsKept, messagesSummarized);
29449
29399
  } else {
29450
29400
  try {
@@ -29454,12 +29404,23 @@ function compactorExtension(pi) {
29454
29404
  const estTokensAfter = Math.round(estTokensBefore * 0.12);
29455
29405
  const charsBefore = estTokensBefore * 4;
29456
29406
  const charsKept = estTokensAfter * 4;
29457
- counters.totalTokensCompacted += estTokensBefore - estTokensAfter;
29407
+ summarized = eventCount;
29408
+ kept = estTokensAfter;
29409
+ tokensSaved = estTokensBefore - estTokensAfter;
29410
+ counters.totalTokensCompacted += tokensSaved;
29458
29411
  sessionDB.addCompactionStats(sessionId, charsBefore, charsKept, eventCount);
29459
29412
  }
29460
29413
  } catch {
29461
29414
  }
29462
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
+ });
29463
29424
  debug("session_compact", { sessionId, tokensBefore, hasCompactionEntry: !!compactionEntry });
29464
29425
  }
29465
29426
  });
@@ -29551,30 +29512,10 @@ function compactorExtension(pi) {
29551
29512
  if (isSandboxTool(tName)) {
29552
29513
  runtimeStats.bytesSandboxed += responseBytes;
29553
29514
  }
29554
- if (isIndexTool(tName)) {
29555
- runtimeStats.bytesIndexed += responseBytes;
29556
- }
29557
29515
  }
29558
29516
  } catch {
29559
29517
  }
29560
29518
  const toolName = event.toolName ?? "";
29561
- const td = config.toolDisplay;
29562
- const toolConfig = {
29563
- readOutputMode: td?.mode,
29564
- searchOutputMode: td?.mode,
29565
- bashOutputMode: td?.mode,
29566
- previewLines: 20,
29567
- bashCollapsedLines: 5,
29568
- showTruncationHints: true
29569
- };
29570
- try {
29571
- const { applyToolDisplayOverride: applyToolDisplayOverride2 } = await Promise.resolve().then(() => (init_tool_overrides(), tool_overrides_exports));
29572
- const override = applyToolDisplayOverride2(toolName, event, toolConfig);
29573
- if (override !== void 0) {
29574
- return override;
29575
- }
29576
- } catch {
29577
- }
29578
29519
  const diffToolNames = ["edit", "Edit", "write", "Write"];
29579
29520
  if (diffToolNames.includes(toolName)) {
29580
29521
  try {
@@ -30883,14 +30824,6 @@ var FooterRenderer = class _FooterRenderer {
30883
30824
  init_core();
30884
30825
  function subscribeToEvents(pi, registry3) {
30885
30826
  const unsubscribers = [];
30886
- unsubscribers.push(
30887
- pi.events.on(UNIPI_EVENTS.COMPACTOR_STATS_UPDATED, (event) => {
30888
- try {
30889
- registry3.updateData("compactor", event);
30890
- } catch {
30891
- }
30892
- })
30893
- );
30894
30827
  unsubscribers.push(
30895
30828
  pi.events.on(UNIPI_EVENTS.COMPACTOR_COMPACTED, (event) => {
30896
30829
  try {