@cortexkit/opencode-magic-context 0.41.2 → 0.41.3

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 (138) hide show
  1. package/README.md +9 -3
  2. package/dist/announcement-3c1vertn.js +14 -0
  3. package/dist/config/project-security.d.ts.map +1 -1
  4. package/dist/config/schema/magic-context.d.ts +756 -4
  5. package/dist/config/schema/magic-context.d.ts.map +1 -1
  6. package/dist/conflict-warning-hook-z5sd697e.js +16 -0
  7. package/dist/derive-budgets-901a0pk0.js +11 -0
  8. package/dist/features/magic-context/compartment-lease.d.ts +6 -0
  9. package/dist/features/magic-context/compartment-lease.d.ts.map +1 -1
  10. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  11. package/dist/features/magic-context/storage-db.d.ts +18 -1
  12. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  13. package/dist/features/magic-context/storage-meta-persisted.d.ts +11 -12
  14. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  15. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  16. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  17. package/dist/features/magic-context/storage-subagent-invocations.d.ts +3 -2
  18. package/dist/features/magic-context/storage-subagent-invocations.d.ts.map +1 -1
  19. package/dist/features/magic-context/storage-tags.d.ts +6 -0
  20. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  21. package/dist/features/magic-context/storage.d.ts +2 -2
  22. package/dist/features/magic-context/storage.d.ts.map +1 -1
  23. package/dist/features/magic-context/subagent-token-capture.d.ts +2 -1
  24. package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +6 -1
  26. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/compartment-runner.d.ts +2 -1
  28. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  29. package/dist/hooks/magic-context/hook.d.ts +4 -3
  30. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/inject-compartments.d.ts +6 -0
  32. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/module-state-sync.d.ts +2 -1
  34. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/module-transport.d.ts +11 -4
  36. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/module-wire.d.ts +6 -1
  38. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/read-session-db.d.ts +15 -0
  40. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  41. package/dist/hooks/magic-context/rust-mode-transform.d.ts +10 -0
  42. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/send-session-notification.d.ts +6 -0
  44. package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/sentinel.d.ts +5 -16
  46. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +32 -0
  49. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  51. package/dist/{index-y1yr57fw.js → index-0bycg0np.js} +18 -18
  52. package/dist/{index-1yh8g550.js → index-22jkk6wg.js} +2 -15
  53. package/dist/{index-dmvy29z0.js → index-3wzdwt6p.js} +11 -15
  54. package/dist/{index-d1mxrd3p.js → index-7k55qm8a.js} +17 -12
  55. package/dist/{index-4x691bz5.js → index-dkv2dn46.js} +5 -2
  56. package/dist/{index-cvda5jb3.js → index-dn1rwpd8.js} +11 -11
  57. package/dist/{index-rgdqj8nj.js → index-e7vx3byk.js} +1 -1
  58. package/dist/{index-szf8w1p5.js → index-fmsr7v69.js} +2 -2
  59. package/dist/{index-d62ypazr.js → index-jdxxq8cz.js} +6 -11
  60. package/dist/{index-9j3tx8hy.js → index-may8skc8.js} +6 -25
  61. package/dist/{index-eq9wr75c.js → index-npx8vhmy.js} +57 -108
  62. package/dist/{index-zwjhba2r.js → index-q264erdp.js} +122 -41
  63. package/dist/{index-b9j85d29.js → index-raatkvw5.js} +49 -11
  64. package/dist/{index-9xexf8s7.js → index-rn7gafjr.js} +1 -9
  65. package/dist/index-v6t0tg85.js +13 -0
  66. package/dist/{index-j8r0ejb3.js → index-vmmjdpc0.js} +1694 -11167
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +2149 -1799
  69. package/dist/model-resolution-dxatzjde.js +12 -0
  70. package/dist/{index-dynqfgx1.js → module-asyncify-2EFITU5U-rkqjhjxd.js} +69 -8
  71. package/dist/plugin/boot-deadline.d.ts +47 -0
  72. package/dist/plugin/boot-deadline.d.ts.map +1 -0
  73. package/dist/plugin/conflict-warning-hook.d.ts +10 -0
  74. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  75. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -0
  76. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  77. package/dist/plugin/messages-transform.d.ts.map +1 -1
  78. package/dist/prompt-context-5y5ms8f9.js +6 -0
  79. package/dist/recomp-orchestrator-f5m2wk74.js +19 -0
  80. package/dist/rpc-notifications-4amf6mf4.js +14 -0
  81. package/dist/{index-vnz6jvdc.js → safe-notification-target-q9z81zt3.js} +4 -3
  82. package/dist/send-session-notification-hdh02wy9.js +16 -0
  83. package/dist/shared/conflict-detector.d.ts +3 -1
  84. package/dist/shared/conflict-detector.d.ts.map +1 -1
  85. package/dist/shared/harness.d.ts +3 -3
  86. package/dist/shared/harness.d.ts.map +1 -1
  87. package/dist/shared/model-resolution.d.ts +3 -3
  88. package/dist/shared/model-resolution.d.ts.map +1 -1
  89. package/dist/shared/prompt-surface-runtime.d.ts +2 -2
  90. package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
  91. package/dist/shared/tag-transcript.d.ts.map +1 -1
  92. package/dist/shared/transcript.d.ts +2 -1
  93. package/dist/shared/transcript.d.ts.map +1 -1
  94. package/dist/storage-meta-session-3rxx9ekm.js +20 -0
  95. package/dist/task-config-hnv93r7p.js +9 -0
  96. package/dist/transformers-node-wasm.js +328 -328
  97. package/dist/transformers-web.js +72 -72
  98. package/dist/{transformers.node-b1wa0hgx.js → transformers.node-dpmf3wpx.js} +74 -74
  99. package/package.json +1 -1
  100. package/src/config/index.test.ts +28 -0
  101. package/src/config/project-security.hostile-matrix.test.ts +78 -9
  102. package/src/config/project-security.ts +4 -3
  103. package/src/config/schema/magic-context.test.ts +64 -0
  104. package/src/config/schema/magic-context.ts +100 -6
  105. package/src/shared/conflict-detector.test.ts +24 -0
  106. package/src/shared/conflict-detector.ts +25 -7
  107. package/src/shared/harness.ts +3 -3
  108. package/src/shared/model-resolution.test.ts +61 -0
  109. package/src/shared/model-resolution.ts +14 -5
  110. package/src/shared/prompt-surface-runtime.ts +3 -3
  111. package/src/shared/tag-transcript.ts +11 -0
  112. package/src/shared/transcript.ts +3 -1
  113. package/dist/announcement-3g9xg433.js +0 -18
  114. package/dist/conflict-warning-hook-rf17b6et.js +0 -17
  115. package/dist/derive-budgets-9q2d3r93.js +0 -14
  116. package/dist/emscripten-module-49k43zfy.js +0 -507
  117. package/dist/emscripten-module-ex4v0x4z.js +0 -606
  118. package/dist/emscripten-module-jfs3d1dq.js +0 -1536
  119. package/dist/emscripten-module-ss4g4spn.js +0 -4563
  120. package/dist/ffi-bbm9vkfa.js +0 -92
  121. package/dist/ffi-kb5r0x1j.js +0 -92
  122. package/dist/ffi-qnrt0y78.js +0 -80
  123. package/dist/ffi-yx2hpf4p.js +0 -80
  124. package/dist/index-8sseka65.js +0 -251
  125. package/dist/index-ss632za9.js +0 -77
  126. package/dist/model-resolution-6w6wzw18.js +0 -17
  127. package/dist/module-ES6BEMUI-1yjdz59c.js +0 -14
  128. package/dist/module-asyncify-2EFITU5U-fdkx4fhy.js +0 -9
  129. package/dist/prompt-context-29qkb28f.js +0 -7
  130. package/dist/recomp-orchestrator-m37p3fq6.js +0 -34
  131. package/dist/rpc-notifications-1ffm755w.js +0 -17
  132. package/dist/safe-notification-target-2a8py95v.js +0 -11
  133. package/dist/send-session-notification-bw7eex6w.js +0 -20
  134. package/dist/storage-meta-session-dse3qv6s.js +0 -23
  135. package/dist/task-config-gv6s04fp.js +0 -18
  136. /package/dist/{index-37yzyeca.js → index-3fe78beq.js} +0 -0
  137. /package/dist/{index-8mtsfhyg.js → index-jfpk81yt.js} +0 -0
  138. /package/dist/{index-2wchnyk6.js → index-kby1sng5.js} +0 -0
@@ -1,15 +1,12 @@
1
- import {
2
- readJsoncFile
3
- } from "./index-8mtsfhyg.js";
4
- import {
5
- waitForSafeNotificationTarget
6
- } from "./index-vnz6jvdc.js";
7
1
  import {
8
2
  log
9
- } from "./index-4x691bz5.js";
3
+ } from "./index-dkv2dn46.js";
10
4
  import {
11
- __require
12
- } from "./index-1yh8g550.js";
5
+ sendIgnoredMessage2
6
+ } from "./index-q264erdp.js";
7
+ import {
8
+ readJsoncFile
9
+ } from "./index-jfpk81yt.js";
13
10
 
14
11
  // src/plugin/conflict-warning-hook.ts
15
12
  import { existsSync, readFileSync } from "node:fs";
@@ -97,11 +94,20 @@ function detectConflicts(directory, options) {
97
94
  };
98
95
  }
99
96
  async function resolveCompactionForBoot(client, timeoutMs = 2000) {
97
+ const controller = new AbortController;
98
+ let timer;
100
99
  try {
101
- const result = await Promise.race([
102
- client.config.get(),
103
- new Promise((_, reject) => setTimeout(() => reject(new Error("config.get() timed out")), timeoutMs))
104
- ]);
100
+ const timeout = new Promise((resolve) => {
101
+ timer = setTimeout(() => {
102
+ controller.abort(new Error("config.get() timed out"));
103
+ resolve(null);
104
+ }, Math.max(0, timeoutMs));
105
+ timer.unref?.();
106
+ });
107
+ const request = Promise.resolve().then(() => client.config.get({ signal: controller.signal }));
108
+ const result = await Promise.race([request, timeout]);
109
+ if (result === null)
110
+ return null;
105
111
  const compaction = result?.data?.compaction;
106
112
  if (typeof compaction?.auto !== "boolean" || typeof compaction?.prune !== "boolean") {
107
113
  log(`[magic-context] conflict-detector: resolved config carried no explicit compaction block (${JSON.stringify(compaction) ?? "absent"}); falling back to file-based detection`);
@@ -110,6 +116,9 @@ async function resolveCompactionForBoot(client, timeoutMs = 2000) {
110
116
  return { auto: compaction.auto, prune: compaction.prune };
111
117
  } catch {
112
118
  return null;
119
+ } finally {
120
+ if (timer)
121
+ clearTimeout(timer);
113
122
  }
114
123
  }
115
124
  function checkCompaction(directory) {
@@ -392,43 +401,21 @@ async function getSessionMessages(client, sessionId) {
392
401
  }
393
402
  return [];
394
403
  }
395
- async function sendConflictWarning(client, directory, conflictResult) {
404
+ async function sendConflictWarning2(client, directory, conflictResult) {
396
405
  const { sessionId } = getDesktopState(directory);
397
406
  if (!sessionId) {
398
407
  log("[magic-context] conflict-warning: could not find active session for Desktop warning");
399
408
  return;
400
409
  }
401
- if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
402
- return;
403
410
  const warningText = formatConflictShort(conflictResult);
404
411
  log(`[magic-context] sending conflict warning to session ${sessionId}: ${conflictResult.reasons.join(", ")}`);
405
412
  try {
406
- const c = client;
407
- const promptInput = {
408
- path: { id: sessionId },
409
- body: {
410
- noReply: true,
411
- parts: [
412
- {
413
- type: "text",
414
- text: warningText,
415
- ignored: true
416
- }
417
- ]
418
- }
419
- };
420
- if (typeof c.session?.prompt === "function") {
421
- await Promise.resolve(c.session.prompt(promptInput));
422
- } else if (typeof c.session?.promptAsync === "function") {
423
- await c.session.promptAsync(promptInput);
424
- } else {
425
- log("[magic-context] conflict-warning: session prompt API unavailable");
426
- }
413
+ await sendIgnoredMessage2(client, sessionId, warningText, {});
427
414
  } catch (error) {
428
415
  log(`[magic-context] conflict-warning: failed to send: ${error instanceof Error ? error.message : String(error)}`);
429
416
  }
430
417
  }
431
- async function cleanupConflictWarnings(client, directory, serverUrl) {
418
+ async function cleanupConflictWarnings2(client, directory, serverUrl) {
432
419
  const { sessionId } = getDesktopState(directory);
433
420
  if (!sessionId) {
434
421
  log("[magic-context] cleanup: no active Desktop session found");
@@ -467,43 +454,33 @@ async function cleanupConflictWarnings(client, directory, serverUrl) {
467
454
  log(`[magic-context] deleted conflict warning message ${messageId}`);
468
455
  }
469
456
  }
470
- if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
471
- return;
472
457
  const enabledText = `${ENABLED_MARKER}. Enjoy! ✨`;
458
+ const scheduleEnabledCleanup = () => {
459
+ setTimeout(async () => {
460
+ try {
461
+ const freshMessages = await getSessionMessages(client, sessionId);
462
+ for (let i = freshMessages.length - 1;i >= 0; i--) {
463
+ const msg = freshMessages[i];
464
+ const msgId = msg.info?.id;
465
+ const msgRole = msg.info?.role;
466
+ if (!msgId || msgRole !== "user")
467
+ break;
468
+ const parts = msg.parts ?? [];
469
+ const isEnabled = parts.length > 0 && parts.every((p) => p.ignored === true && p.type === "text" && typeof p.text === "string" && p.text.startsWith(ENABLED_MARKER));
470
+ if (isEnabled) {
471
+ await deleteMessage(serverUrl, sessionId, msgId);
472
+ } else {
473
+ break;
474
+ }
475
+ }
476
+ } catch {}
477
+ }, 1000);
478
+ };
473
479
  try {
474
- const c = client;
475
- const promptInput = {
476
- path: { id: sessionId },
477
- body: {
478
- noReply: true,
479
- parts: [{ type: "text", text: enabledText, ignored: true }]
480
- }
481
- };
482
- if (typeof c.session?.prompt === "function") {
483
- await Promise.resolve(c.session.prompt(promptInput));
484
- } else if (typeof c.session?.promptAsync === "function") {
485
- await c.session.promptAsync(promptInput);
486
- }
480
+ await sendIgnoredMessage2(client, sessionId, enabledText, {
481
+ onDelivered: scheduleEnabledCleanup
482
+ });
487
483
  } catch {}
488
- setTimeout(async () => {
489
- try {
490
- const freshMessages = await getSessionMessages(client, sessionId);
491
- for (let i = freshMessages.length - 1;i >= 0; i--) {
492
- const msg = freshMessages[i];
493
- const msgId = msg.info?.id;
494
- const msgRole = msg.info?.role;
495
- if (!msgId || msgRole !== "user")
496
- break;
497
- const parts = msg.parts ?? [];
498
- const isEnabled = parts.length > 0 && parts.every((p) => p.ignored === true && p.type === "text" && typeof p.text === "string" && p.text.startsWith(ENABLED_MARKER));
499
- if (isEnabled) {
500
- await deleteMessage(serverUrl, sessionId, msgId);
501
- } else {
502
- break;
503
- }
504
- }
505
- } catch {}
506
- }, 1000);
507
484
  }
508
485
  async function cleanupEnabledMessages(messages, serverUrl, sessionId) {
509
486
  if (!serverUrl)
@@ -523,12 +500,10 @@ async function cleanupEnabledMessages(messages, serverUrl, sessionId) {
523
500
  }
524
501
  }
525
502
  }
526
- async function sendSchemaFenceWarning(client, directory, detail) {
503
+ async function sendSchemaFenceWarning2(client, directory, detail) {
527
504
  const { sessionId } = getDesktopState(directory);
528
505
  if (!sessionId)
529
506
  return;
530
- if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
531
- return;
532
507
  const text = [
533
508
  `${SCHEMA_FENCE_MARKER}`,
534
509
  "",
@@ -546,32 +521,21 @@ async function sendSchemaFenceWarning(client, directory, detail) {
546
521
  ].join(`
547
522
  `);
548
523
  try {
549
- const c = client;
550
- const promptInput = {
551
- path: { id: sessionId },
552
- body: { noReply: true, parts: [{ type: "text", text, ignored: true }] }
553
- };
554
- if (typeof c.session?.prompt === "function") {
555
- await Promise.resolve(c.session.prompt(promptInput));
556
- } else if (typeof c.session?.promptAsync === "function") {
557
- await c.session.promptAsync(promptInput);
558
- }
524
+ await sendIgnoredMessage2(client, sessionId, text, {});
559
525
  } catch {
560
526
  return;
561
527
  }
562
528
  }
563
- async function sendStartupAnnouncement(client, directory, version, features, footer, markSeen) {
529
+ async function sendStartupAnnouncement2(client, directory, version, features, footer, markSeen) {
564
530
  if (!version || features.length === 0)
565
531
  return;
566
532
  const { sessionId } = getDesktopState(directory);
567
533
  if (!sessionId) {
568
534
  return;
569
535
  }
570
- const { isTuiConnected } = await import("./rpc-notifications-1ffm755w.js");
536
+ const { isTuiConnected } = await import("./rpc-notifications-4amf6mf4.js");
571
537
  if (isTuiConnected(sessionId) || isTuiConnected())
572
538
  return;
573
- if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
574
- return;
575
539
  const bullets = features.map((line) => ` • ${line}`).join(`
576
540
  `);
577
541
  const sections = [`${ANNOUNCEMENT_MARKER} v${version}:`, "", bullets];
@@ -582,27 +546,12 @@ async function sendStartupAnnouncement(client, directory, version, features, foo
582
546
  `);
583
547
  log(`[magic-context] sending startup announcement for v${version} to session ${sessionId}`);
584
548
  try {
585
- const c = client;
586
- const promptInput = {
587
- path: { id: sessionId },
588
- body: {
589
- noReply: true,
590
- parts: [{ type: "text", text, ignored: true }]
591
- }
592
- };
593
- if (typeof c.session?.prompt === "function") {
594
- await Promise.resolve(c.session.prompt(promptInput));
595
- } else if (typeof c.session?.promptAsync === "function") {
596
- await c.session.promptAsync(promptInput);
597
- } else {
598
- log("[magic-context] announcement: session prompt API unavailable");
599
- return;
600
- }
549
+ await sendIgnoredMessage2(client, sessionId, text, {
550
+ onDelivered: () => markSeen(version)
551
+ });
601
552
  } catch (error) {
602
553
  log(`[magic-context] announcement: failed to send: ${error instanceof Error ? error.message : String(error)}`);
603
- return;
604
554
  }
605
- markSeen(version);
606
555
  }
607
556
 
608
- export { detectConflicts, resolveCompactionForBoot, sendConflictWarning, cleanupConflictWarnings, sendSchemaFenceWarning, sendStartupAnnouncement };
557
+ export { detectConflicts, resolveCompactionForBoot, sendConflictWarning2, cleanupConflictWarnings2, sendSchemaFenceWarning2, sendStartupAnnouncement2 };
@@ -1,12 +1,9 @@
1
- import {
2
- isMidTurn
3
- } from "./index-b9j85d29.js";
4
1
  import {
5
2
  sessionLog
6
- } from "./index-4x691bz5.js";
3
+ } from "./index-dkv2dn46.js";
7
4
  import {
8
- __require
9
- } from "./index-1yh8g550.js";
5
+ shouldHoldIgnoredNotification
6
+ } from "./index-raatkvw5.js";
10
7
 
11
8
  // src/shared/error-message.ts
12
9
  function getErrorMessage(error) {
@@ -88,7 +85,14 @@ function safeString(value) {
88
85
  var MAX_QUEUED_IGNORED_NOTIFICATIONS = 16;
89
86
  var queuedIgnoredNotifications = new Map;
90
87
  var flushingIgnoredNotifications = new Set;
91
- var midTurnDetector = (sessionId) => isMidTurn(undefined, sessionId);
88
+ var midTurnDetector = (sessionId) => shouldHoldIgnoredNotification(sessionId);
89
+ var notificationServerUrl;
90
+ var noticeDeleter;
91
+ var notificationDiagnosticObserverForTests;
92
+ function notificationDiagnostic(sessionId, message) {
93
+ notificationDiagnosticObserverForTests?.(message);
94
+ sessionLog(sessionId, message);
95
+ }
92
96
  function queueIgnoredNotification(notification) {
93
97
  const queued = queuedIgnoredNotifications.get(notification.sessionId) ?? [];
94
98
  queued.push(notification);
@@ -98,6 +102,10 @@ function queueIgnoredNotification(notification) {
98
102
  }
99
103
  queuedIgnoredNotifications.set(notification.sessionId, queued);
100
104
  }
105
+ function holdIgnoredNotification(notification, checkpoint) {
106
+ queueIgnoredNotification(notification);
107
+ notificationDiagnostic(notification.sessionId, `ignored notification held ${checkpoint} (session active); queued`);
108
+ }
101
109
  async function trySendTuiToast(sessionId, text, params, forcePersist) {
102
110
  if (forcePersist)
103
111
  return false;
@@ -105,11 +113,11 @@ async function trySendTuiToast(sessionId, text, params, forcePersist) {
105
113
  const message = text.length > 200 ? `${text.slice(0, 200)}…` : text;
106
114
  const toastVariant = inferToastVariant(text);
107
115
  const duration = params.toastDurationMs ?? 5000;
108
- const { isTuiConnected: checkTui } = await import("./rpc-notifications-1ffm755w.js");
116
+ const { isTuiConnected: checkTui } = await import("./rpc-notifications-4amf6mf4.js");
109
117
  if (!checkTui(sessionId))
110
118
  return false;
111
119
  try {
112
- const { pushNotification } = await import("./rpc-notifications-1ffm755w.js");
120
+ const { pushNotification } = await import("./rpc-notifications-4amf6mf4.js");
113
121
  pushNotification("toast", {
114
122
  title,
115
123
  message,
@@ -122,19 +130,16 @@ async function trySendTuiToast(sessionId, text, params, forcePersist) {
122
130
  return false;
123
131
  }
124
132
  }
125
- var __ignoredNotificationTest = {
126
- pendingTexts(sessionId) {
127
- return (queuedIgnoredNotifications.get(sessionId) ?? []).map((item) => item.text);
128
- },
129
- reset() {
130
- queuedIgnoredNotifications.clear();
131
- flushingIgnoredNotifications.clear();
132
- midTurnDetector = (sessionId) => isMidTurn(undefined, sessionId);
133
- },
134
- setMidTurnDetector(detector) {
135
- midTurnDetector = detector;
133
+ function setNotificationServerUrl2(url) {
134
+ notificationServerUrl = typeof url === "string" && url.length > 0 ? url.replace(/\/$/, "") : undefined;
135
+ }
136
+ function notifyDelivered(sessionId, params) {
137
+ try {
138
+ params.onDelivered?.();
139
+ } catch (error) {
140
+ sessionLog(sessionId, "notification delivery callback failed:", getErrorMessage(error));
136
141
  }
137
- };
142
+ }
138
143
  function hasNotificationSessionClient(client) {
139
144
  if (client === null || typeof client !== "object")
140
145
  return false;
@@ -168,16 +173,16 @@ function extractToastTitle(text) {
168
173
  }
169
174
  async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersist) {
170
175
  if (midTurnDetector(sessionId)) {
171
- queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
176
+ holdIgnoredNotification({ client, sessionId, text, params, forcePersist }, "during target checks");
172
177
  return "queued";
173
178
  }
174
- const { waitForSafeNotificationTarget } = await import("./safe-notification-target-2a8py95v.js");
179
+ const { waitForSafeNotificationTarget } = await import("./safe-notification-target-q9z81zt3.js");
175
180
  if (await waitForSafeNotificationTarget(client, sessionId) === "skip") {
176
181
  sessionLog(sessionId, "notification skipped (session not titled yet)");
177
182
  return "skipped";
178
183
  }
179
184
  if (midTurnDetector(sessionId)) {
180
- queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
185
+ holdIgnoredNotification({ client, sessionId, text, params, forcePersist }, "during target checks");
181
186
  return "queued";
182
187
  }
183
188
  if (!hasNotificationSessionClient(client)) {
@@ -190,7 +195,7 @@ async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersi
190
195
  let model = params.providerId && params.modelId ? { providerID: params.providerId, modelID: params.modelId } : undefined;
191
196
  if (!agent || !model || !variant) {
192
197
  try {
193
- const { resolvePromptContext } = await import("./prompt-context-29qkb28f.js");
198
+ const { resolvePromptContext } = await import("./prompt-context-5y5ms8f9.js");
194
199
  const resolved = await resolvePromptContext(client, sessionId);
195
200
  if (resolved) {
196
201
  agent = agent ?? resolved.agent;
@@ -200,7 +205,7 @@ async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersi
200
205
  } catch {}
201
206
  }
202
207
  if (midTurnDetector(sessionId)) {
203
- queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
208
+ holdIgnoredNotification({ client, sessionId, text, params, forcePersist }, "before append");
204
209
  return "queued";
205
210
  }
206
211
  const input = {
@@ -220,32 +225,108 @@ async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersi
220
225
  }
221
226
  };
222
227
  try {
228
+ let result;
223
229
  if (typeof c.session?.prompt === "function") {
224
- await Promise.resolve(c.session.prompt(input));
225
- return "sent";
230
+ result = await Promise.resolve(c.session.prompt(input));
231
+ } else if (typeof c.session?.promptAsync === "function") {
232
+ result = await c.session.promptAsync(input);
233
+ } else {
234
+ sessionLog(sessionId, "session prompt API unavailable for notification");
235
+ return "failed";
226
236
  }
227
- if (typeof c.session?.promptAsync === "function") {
228
- await c.session.promptAsync(input);
229
- return "sent";
237
+ const messageId = extractPromptedMessageId(result);
238
+ if (await revertNoticeIfUnsafe({
239
+ client,
240
+ sessionId,
241
+ text,
242
+ params,
243
+ forcePersist,
244
+ messageId
245
+ })) {
246
+ return "queued";
230
247
  }
231
- sessionLog(sessionId, "session prompt API unavailable for notification");
232
- return "failed";
248
+ notifyDelivered(sessionId, params);
249
+ return "sent";
233
250
  } catch (error) {
234
251
  const msg = getErrorMessage(error);
235
252
  sessionLog(sessionId, "failed to send notification:", msg);
236
253
  return "failed";
237
254
  }
238
255
  }
239
- async function sendIgnoredMessage(client, sessionId, text, params, forcePersist = false) {
240
- if (await trySendTuiToast(sessionId, text, params, forcePersist))
256
+ function extractPromptedMessageId(result) {
257
+ if (result === null || typeof result !== "object")
258
+ return;
259
+ const root = result;
260
+ const data = root.data !== null && typeof root.data === "object" ? root.data : root;
261
+ const info = data.info !== null && typeof data.info === "object" ? data.info : data;
262
+ return typeof info.id === "string" && info.id.length > 0 ? info.id : undefined;
263
+ }
264
+ function getServerAuth() {
265
+ const password = process.env.OPENCODE_SERVER_PASSWORD;
266
+ if (!password)
267
+ return;
268
+ const username = process.env.OPENCODE_SERVER_USERNAME ?? "opencode";
269
+ return `Basic ${Buffer.from(`${username}:${password}`, "utf8").toString("base64")}`;
270
+ }
271
+ async function deleteNoticeMessage(sessionId, messageId) {
272
+ if (noticeDeleter)
273
+ return noticeDeleter(sessionId, messageId);
274
+ if (!notificationServerUrl) {
275
+ sessionLog(sessionId, "cannot roll back notice: no server URL for session.message.delete");
276
+ return false;
277
+ }
278
+ const url = `${notificationServerUrl}/session/${encodeURIComponent(sessionId)}/message/${encodeURIComponent(messageId)}`;
279
+ try {
280
+ const auth = getServerAuth();
281
+ const response = await fetch(url, {
282
+ method: "DELETE",
283
+ headers: auth ? { Authorization: auth } : {},
284
+ signal: AbortSignal.timeout(1e4)
285
+ });
286
+ if (!response.ok) {
287
+ sessionLog(sessionId, `notice rollback DELETE failed status=${response.status} url=${url}`);
288
+ return false;
289
+ }
290
+ return true;
291
+ } catch (error) {
292
+ sessionLog(sessionId, "notice rollback DELETE error:", getErrorMessage(error));
293
+ return false;
294
+ }
295
+ }
296
+ async function revertNoticeIfUnsafe(notification) {
297
+ if (!midTurnDetector(notification.sessionId))
298
+ return false;
299
+ if (notification.messageId) {
300
+ const deleted = await deleteNoticeMessage(notification.sessionId, notification.messageId);
301
+ if (deleted) {
302
+ notificationDiagnostic(notification.sessionId, `notice rolled back (deleted row ${notification.messageId}); queued for idle delivery`);
303
+ } else {
304
+ sessionLog(notification.sessionId, "failed to roll back a notice that landed while a run was active");
305
+ }
306
+ } else {
307
+ sessionLog(notification.sessionId, "notice landed while a run was active but the new row id was not returned");
308
+ }
309
+ queueIgnoredNotification({
310
+ client: notification.client,
311
+ sessionId: notification.sessionId,
312
+ text: notification.text,
313
+ params: notification.params,
314
+ forcePersist: notification.forcePersist
315
+ });
316
+ return true;
317
+ }
318
+ async function sendIgnoredMessage2(client, sessionId, text, params, forcePersist = false) {
319
+ if (await trySendTuiToast(sessionId, text, params, forcePersist)) {
320
+ notifyDelivered(sessionId, params);
241
321
  return "sent";
322
+ }
242
323
  if (midTurnDetector(sessionId)) {
243
- queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
324
+ holdIgnoredNotification({ client, sessionId, text, params, forcePersist }, "before target checks");
244
325
  return "queued";
245
326
  }
246
327
  return sendIgnoredMessageNow(client, sessionId, text, params, forcePersist);
247
328
  }
248
- async function flushIgnoredMessages(sessionId) {
329
+ async function flushIgnoredMessages2(sessionId) {
249
330
  if (flushingIgnoredNotifications.has(sessionId) || midTurnDetector(sessionId))
250
331
  return;
251
332
  const queued = queuedIgnoredNotifications.get(sessionId);
@@ -255,7 +336,7 @@ async function flushIgnoredMessages(sessionId) {
255
336
  flushingIgnoredNotifications.add(sessionId);
256
337
  try {
257
338
  for (const notification of queued) {
258
- const disposition = await sendIgnoredMessage(notification.client, notification.sessionId, notification.text, notification.params, notification.forcePersist);
339
+ const disposition = await sendIgnoredMessage2(notification.client, notification.sessionId, notification.text, notification.params, notification.forcePersist);
259
340
  if (disposition === "queued") {
260
341
  for (const remaining of queued.slice(queued.indexOf(notification) + 1)) {
261
342
  queueIgnoredNotification(remaining);
@@ -267,11 +348,11 @@ async function flushIgnoredMessages(sessionId) {
267
348
  flushingIgnoredNotifications.delete(sessionId);
268
349
  }
269
350
  }
270
- function clearIgnoredMessages(sessionId) {
351
+ function clearIgnoredMessages2(sessionId) {
271
352
  queuedIgnoredNotifications.delete(sessionId);
272
353
  flushingIgnoredNotifications.delete(sessionId);
273
354
  }
274
- async function sendUserPrompt(client, sessionId, text) {
355
+ async function sendUserPrompt2(client, sessionId, text) {
275
356
  if (!hasNotificationSessionClient(client)) {
276
357
  sessionLog(sessionId, "session prompt API unavailable for user prompt");
277
358
  return;
@@ -297,4 +378,4 @@ async function sendUserPrompt(client, sessionId, text) {
297
378
  }
298
379
  }
299
380
 
300
- export { getErrorMessage, describeError, MAX_QUEUED_IGNORED_NOTIFICATIONS, __ignoredNotificationTest, sendIgnoredMessage, flushIgnoredMessages, clearIgnoredMessages, sendUserPrompt };
381
+ export { getErrorMessage, describeError, setNotificationServerUrl2, sendIgnoredMessage2, flushIgnoredMessages2, clearIgnoredMessages2, sendUserPrompt2 };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log
3
- } from "./index-4x691bz5.js";
3
+ } from "./index-dkv2dn46.js";
4
4
  import {
5
5
  getDataDir
6
- } from "./index-37yzyeca.js";
6
+ } from "./index-3fe78beq.js";
7
7
 
8
8
  // src/shared/sqlite-helpers.ts
9
9
  function closeQuietly(db) {
@@ -223,15 +223,29 @@ function isMidTurn(_deps, sessionId) {
223
223
  return false;
224
224
  }
225
225
  }
226
+ function shouldHoldIgnoredNotificationFromOpenCodeDb(db, sessionId) {
227
+ if (isMidTurnFromOpenCodeDb(db, sessionId))
228
+ return true;
229
+ if (hasUnfinishedAssistant(db, sessionId))
230
+ return true;
231
+ if (hasUnansweredRealUser(db, sessionId))
232
+ return true;
233
+ return false;
234
+ }
235
+ function shouldHoldIgnoredNotification(sessionId) {
236
+ if (process.env.MAGIC_CONTEXT_NOTICE_GATE === "bypass")
237
+ return false;
238
+ if (process.env.MAGIC_CONTEXT_NOTICE_GATE === "hold")
239
+ return true;
240
+ try {
241
+ return withReadOnlySessionDb((db) => shouldHoldIgnoredNotificationFromOpenCodeDb(db, sessionId));
242
+ } catch (error) {
243
+ log("[magic-context] failed to inspect notice-hold state:", error);
244
+ return false;
245
+ }
246
+ }
226
247
  function isMidTurnFromOpenCodeDb(db, sessionId) {
227
- const latestAssistant = db.prepare(`SELECT id,
228
- json_extract(data, '$.finish') as finish,
229
- time_created as timeCreated
230
- FROM message
231
- WHERE session_id = ?
232
- AND json_extract(data, '$.role') = 'assistant'
233
- ORDER BY time_created DESC
234
- LIMIT 1`).get(sessionId);
248
+ const latestAssistant = latestAssistantRow(db, sessionId);
235
249
  if (typeof latestAssistant?.id !== "string")
236
250
  return false;
237
251
  if (hasNewerRealUserMessage(db, sessionId, latestAssistant.timeCreated))
@@ -271,6 +285,30 @@ function hasNewerRealUserMessage(db, sessionId, latestAssistantTimeCreated) {
271
285
  LIMIT 1`).get(sessionId, latestAssistantTimeCreated);
272
286
  return row?.one === 1;
273
287
  }
288
+ function latestAssistantRow(db, sessionId) {
289
+ return db.prepare(`SELECT id,
290
+ json_extract(data, '$.finish') as finish,
291
+ time_created as timeCreated
292
+ FROM message
293
+ WHERE session_id = ?
294
+ AND json_extract(data, '$.role') = 'assistant'
295
+ ORDER BY time_created DESC
296
+ LIMIT 1`).get(sessionId);
297
+ }
298
+ function hasUnfinishedAssistant(db, sessionId) {
299
+ const latestAssistant = latestAssistantRow(db, sessionId);
300
+ if (typeof latestAssistant?.id !== "string")
301
+ return false;
302
+ const finish = latestAssistant.finish;
303
+ if (typeof finish !== "string" || finish.length === 0)
304
+ return true;
305
+ return finish === "tool-calls" || finish === "unknown";
306
+ }
307
+ function hasUnansweredRealUser(db, sessionId) {
308
+ const latestAssistant = latestAssistantRow(db, sessionId);
309
+ const latestAssistantTime = typeof latestAssistant?.timeCreated === "number" ? latestAssistant.timeCreated : -1;
310
+ return hasNewerRealUserMessage(db, sessionId, latestAssistantTime);
311
+ }
274
312
  function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
275
313
  const result = new Map;
276
314
  if (messageIds.length === 0)
@@ -319,4 +357,4 @@ function findLastAssistantModelFromOpenCodeDb(sessionId) {
319
357
  }
320
358
  }
321
359
 
322
- export { Database, withPrivilegedWriter, closeQuietly, openCodeDbExists, withReadOnlySessionDb, getRawSessionMessageCountFromDb, isMidTurn, getMessageTimesFromOpenCodeDb, findLastAssistantModelFromOpenCodeDb };
360
+ export { detectSqliteRuntime, Database, withPrivilegedWriter, closeQuietly, openCodeDbExists, withReadOnlySessionDb, getRawSessionMessageCountFromDb, isMidTurn, shouldHoldIgnoredNotification, getMessageTimesFromOpenCodeDb, findLastAssistantModelFromOpenCodeDb };
@@ -1,16 +1,8 @@
1
1
  // ../../node_modules/.bun/@jitl+quickjs-ffi-types@0.32.0/node_modules/@jitl/quickjs-ffi-types/dist/index.mjs
2
- function assertSync(fn) {
3
- return function(...args) {
4
- let result = fn(...args);
5
- if (result && typeof result == "object" && result instanceof Promise)
6
- throw new Error("Function unexpectedly returned a Promise");
7
- return result;
8
- };
9
- }
10
2
  var EvalFlags = { JS_EVAL_TYPE_GLOBAL: 0, JS_EVAL_TYPE_MODULE: 1, JS_EVAL_TYPE_DIRECT: 2, JS_EVAL_TYPE_INDIRECT: 3, JS_EVAL_TYPE_MASK: 3, JS_EVAL_FLAG_STRICT: 8, JS_EVAL_FLAG_STRIP: 16, JS_EVAL_FLAG_COMPILE_ONLY: 32, JS_EVAL_FLAG_BACKTRACE_BARRIER: 64 };
11
3
  var IntrinsicsFlags = { BaseObjects: 1, Date: 2, Eval: 4, StringNormalize: 8, RegExp: 16, RegExpCompiler: 32, JSON: 64, Proxy: 128, MapSet: 256, TypedArrays: 512, Promise: 1024, BigInt: 2048, BigFloat: 4096, BigDecimal: 8192, OperatorOverloading: 16384, BignumExt: 32768 };
12
4
  var JSPromiseStateEnum = { Pending: 0, Fulfilled: 1, Rejected: 2 };
13
5
  var GetOwnPropertyNamesFlags = { JS_GPN_STRING_MASK: 1, JS_GPN_SYMBOL_MASK: 2, JS_GPN_PRIVATE_MASK: 4, JS_GPN_ENUM_ONLY: 16, JS_GPN_SET_ENUM: 32, QTS_GPN_NUMBER_MASK: 64, QTS_STANDARD_COMPLIANT_NUMBER: 128 };
14
6
  var IsEqualOp = { IsStrictlyEqual: 0, IsSameValue: 1, IsSameValueZero: 2 };
15
7
 
16
- export { assertSync, EvalFlags, IntrinsicsFlags, JSPromiseStateEnum, GetOwnPropertyNamesFlags, IsEqualOp };
8
+ export { EvalFlags, IntrinsicsFlags, JSPromiseStateEnum, GetOwnPropertyNamesFlags, IsEqualOp };
@@ -0,0 +1,13 @@
1
+ // ../../node_modules/.bun/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/index.mjs
2
+ async function newQuickJSAsyncWASMModuleFromVariant(variantOrPromise) {
3
+ let variant = smartUnwrap(await variantOrPromise), [wasmModuleLoader, QuickJSAsyncFFI, { QuickJSAsyncWASMModule: QuickJSAsyncWASMModule2 }] = await Promise.all([variant.importModuleLoader().then(smartUnwrap), variant.importFFI(), import("./module-asyncify-2EFITU5U-rkqjhjxd.js").then(smartUnwrap)]), wasmModule = await wasmModuleLoader();
4
+ wasmModule.type = "async";
5
+ let ffi = new QuickJSAsyncFFI(wasmModule);
6
+ return new QuickJSAsyncWASMModule2(wasmModule, ffi);
7
+ }
8
+ function smartUnwrap(val) {
9
+ return val && "default" in val && val.default ? val.default && "default" in val.default && val.default.default ? val.default.default : val.default : val;
10
+ }
11
+ export {
12
+ newQuickJSAsyncWASMModuleFromVariant
13
+ };