@xopcai/xopc 0.0.228 → 0.0.230

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 (135) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CjGZBs4C.js → agent-avatar-dicebear-Bp_wmuGW.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agents-B0NI4_NI.js → agents-CKG93dlc.js} +1 -1
  5. package/dist/gateway/static/root/assets/{app-management-settings-panel--SjycrXk.js → app-management-settings-panel-CwAPK4Jm.js} +1 -1
  6. package/dist/gateway/static/root/assets/{appearance-settings-8G-TVUtg.js → appearance-settings-DsGLvqTb.js} +1 -1
  7. package/dist/gateway/static/root/assets/{apps-page-2NiqD-YP.js → apps-page-Ee9qOQEx.js} +1 -1
  8. package/dist/gateway/static/root/assets/{attachment-load-Bx44zSpz.js → attachment-load-B4qqigAQ.js} +1 -1
  9. package/dist/gateway/static/root/assets/{automations-page-CrKAHqc0.js → automations-page-B8ImehSW.js} +1 -1
  10. package/dist/gateway/static/root/assets/{automations-workspace-Q3g9cf0h.js → automations-workspace-BNFAfNwC.js} +1 -1
  11. package/dist/gateway/static/root/assets/{binary-plugins-DjDwnHAQ.js → binary-plugins-DaJbyYc2.js} +1 -1
  12. package/dist/gateway/static/root/assets/{block-editor-7aMOKoZM.js → block-editor-Kl25XWJc.js} +1 -1
  13. package/dist/gateway/static/root/assets/{browser-settings-page-DTwPkmAC.js → browser-settings-page-Cg6NgwE_.js} +1 -1
  14. package/dist/gateway/static/root/assets/{browser-workflows-page-DVLrWKz_.js → browser-workflows-page-DjMxs33B.js} +1 -1
  15. package/dist/gateway/static/root/assets/{capabilities-settings-panel-YcP4FbN7.js → capabilities-settings-panel-NvJKgF1u.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-AcPjHHia.js → capability-presets-settings-panel-C2uBdALe.js} +1 -1
  17. package/dist/gateway/static/root/assets/{channels-settings-Ca94Q1D-.js → channels-settings-CIcYKMEs.js} +1 -1
  18. package/dist/gateway/static/root/assets/{chat-terminal-dock-BR-_7KC0.js → chat-terminal-dock-BiU_4Rq6.js} +1 -1
  19. package/dist/gateway/static/root/assets/{connectors-page-SGxfLTd6.js → connectors-page-CEEcmUiP.js} +1 -1
  20. package/dist/gateway/static/root/assets/{cron-expression-D8-c-Tkw.js → cron-expression-Cs_5pUPT.js} +1 -1
  21. package/dist/gateway/static/root/assets/{date-picker-kV-Jyl9J.js → date-picker-y1kWrFVU.js} +1 -1
  22. package/dist/gateway/static/root/assets/{dependency-picker-CwKcrfGj.js → dependency-picker-skc62zrn.js} +1 -1
  23. package/dist/gateway/static/root/assets/{desktop-pet-settings-CTs5WGzk.js → desktop-pet-settings-BVVCBgYK.js} +1 -1
  24. package/dist/gateway/static/root/assets/{desktop-pet-D9rnchu3.js → desktop-pet-ux9DxchT.js} +1 -1
  25. package/dist/gateway/static/root/assets/{directory-picker-path-field-CCl0aaDm.js → directory-picker-path-field-V8vZP0Dy.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-debug-page-Cau40SvC.js → extension-debug-page-Dh6DDuvQ.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-page-BsJXdvIL.js → extension-page-8OKQDEat.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-settings-page-qWqJyF7-.js → extension-settings-page-BgSV0Nny.js} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-config-api-BI0SELXm.js → gateway-config-api-Dlc6eaTG.js} +1 -1
  30. package/dist/gateway/static/root/assets/{gateway-settings-6ZdOIC7F.js → gateway-settings-DqQgiAUv.js} +1 -1
  31. package/dist/gateway/static/root/assets/{heartbeat-settings-KBwhXeFD.js → heartbeat-settings-Pxqk5GFH.js} +1 -1
  32. package/dist/gateway/static/root/assets/{home-page-BA2M4CLi.js → home-page-1xjo6wE4.js} +1 -1
  33. package/dist/gateway/static/root/assets/{index-BX0dz1JK.js → index-Bbd_dXui.js} +17 -17
  34. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-kHtpnyEm.js → keyboard-shortcuts-settings-BSFNqxzY.js} +1 -1
  35. package/dist/gateway/static/root/assets/{local-app-workbench-page-D2nkn09R.js → local-app-workbench-page-heQ4-PJ7.js} +1 -1
  36. package/dist/gateway/static/root/assets/{local-apps-page-BCXa0oUa.js → local-apps-page-DzU6yZ9-.js} +1 -1
  37. package/dist/gateway/static/root/assets/{logs-page-D3sNst0K.js → logs-page-BblnS6Jy.js} +1 -1
  38. package/dist/gateway/static/root/assets/{management-settings-tHnlfOAQ.js → management-settings-B8hM56HS.js} +1 -1
  39. package/dist/gateway/static/root/assets/{markdown-split-BMLqHHr5.js → markdown-split-DRbZjxMK.js} +1 -1
  40. package/dist/gateway/static/root/assets/{media-plugins-DybwqTgv.js → media-plugins-B8vMe5VX.js} +1 -1
  41. package/dist/gateway/static/root/assets/{notes-page-C24SfF6r.js → notes-page-B-YZ7iuK.js} +1 -1
  42. package/dist/gateway/static/root/assets/{notes-workbench-CTdZc3ov.js → notes-workbench-CAM09C8b.js} +2 -2
  43. package/dist/gateway/static/root/assets/{product-open-page-DBfCVlJq.js → product-open-page-DkWcyki5.js} +1 -1
  44. package/dist/gateway/static/root/assets/{project-detail-page-COHaEpbA.js → project-detail-page-BIPIwgU2.js} +1 -1
  45. package/dist/gateway/static/root/assets/{projects-page-lMnST7D_.js → projects-page-B_kzTn1K.js} +1 -1
  46. package/dist/gateway/static/root/assets/{remote-access-hub-BIRKBvO_.js → remote-access-hub-CtD8DsD-.js} +1 -1
  47. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BPEGTip5.js → runtime-tools-settings-panel-Bq_ETeal.js} +1 -1
  48. package/dist/gateway/static/root/assets/{schema-form-Dxlu60Td.js → schema-form-BogTlrsP.js} +1 -1
  49. package/dist/gateway/static/root/assets/{sessions-page-CATO9G3y.js → sessions-page-B8n9HI1y.js} +1 -1
  50. package/dist/gateway/static/root/assets/settings-advanced-gate-xlhdRTME.js +1 -0
  51. package/dist/gateway/static/root/assets/{settings-form-section-ChXh3Bwk.js → settings-form-section-Coy1Cbp6.js} +1 -1
  52. package/dist/gateway/static/root/assets/{settings-loading-skeleton-BCAUqIe2.js → settings-loading-skeleton-B5GU7iCG.js} +1 -1
  53. package/dist/gateway/static/root/assets/{settings-page-D25W9Okr.js → settings-page-Dd-BRu12.js} +1 -1
  54. package/dist/gateway/static/root/assets/{setup-status-panel-DsN7Z4Fl.js → setup-status-panel-CwEFAVWx.js} +1 -1
  55. package/dist/gateway/static/root/assets/{share-preview-page-CRFnUaNb.js → share-preview-page-DzXw9UQJ.js} +1 -1
  56. package/dist/gateway/static/root/assets/{shares-settings-CAX0eNaP.js → shares-settings-Bk6UQOwS.js} +1 -1
  57. package/dist/gateway/static/root/assets/{skill-reload-api-Doy1gghe.js → skill-reload-api-DrmQZ3Au.js} +1 -1
  58. package/dist/gateway/static/root/assets/{skills-page-Cl7jjTK7.js → skills-page-Brxb6QnW.js} +1 -1
  59. package/dist/gateway/static/root/assets/{skills-page.utils-BiCl4nb5.js → skills-page.utils-FB5LtEGm.js} +1 -1
  60. package/dist/gateway/static/root/assets/{system-settings-panel-BuESR7ZT.js → system-settings-panel-ChosWt8n.js} +1 -1
  61. package/dist/gateway/static/root/assets/{task-detail-page-BRiy8NL3.js → task-detail-page-CW2sJbdc.js} +1 -1
  62. package/dist/gateway/static/root/assets/{use-autosave-Dg1mgJ_l.js → use-autosave-2SqnmSKD.js} +1 -1
  63. package/dist/gateway/static/root/assets/{user-context-page-CqoADNKu.js → user-context-page-Bq9KB-Oj.js} +1 -1
  64. package/dist/gateway/static/root/assets/{voice-api-key-field-BDNexlVR.js → voice-api-key-field-DtTIF6M8.js} +1 -1
  65. package/dist/gateway/static/root/assets/{work-discovery-overlay-BHTfeaWL.js → work-discovery-overlay-CK3aZDdE.js} +1 -1
  66. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cf_w-NZy.js → workflow-run-setup-panel-BjOuddkX.js} +1 -1
  67. package/dist/gateway/static/root/assets/{workflows-page-j0P-uECz.js → workflows-page-CcbGEQol.js} +1 -1
  68. package/dist/gateway/static/root/index.html +1 -1
  69. package/dist/package.js +1 -1
  70. package/dist/src/agent/agent-instance-gateway.d.ts +1 -1
  71. package/dist/src/agent/agent-manager.d.ts +1 -1
  72. package/dist/src/agent/agent-manager.js +18 -3
  73. package/dist/src/agent/background-review/prompts.d.ts +8 -2
  74. package/dist/src/agent/background-review/prompts.js +25 -12
  75. package/dist/src/agent/background-review/run-background-review.d.ts +7 -2
  76. package/dist/src/agent/background-review/run-background-review.js +175 -160
  77. package/dist/src/agent/inbound/turn-dispatcher.js +1 -1
  78. package/dist/src/agent/memory/index.d.ts +1 -1
  79. package/dist/src/agent/memory/index.js +2 -2
  80. package/dist/src/agent/memory/manager.d.ts +1 -11
  81. package/dist/src/agent/memory/manager.js +0 -16
  82. package/dist/src/agent/memory/understanding/service.d.ts +0 -11
  83. package/dist/src/agent/memory/understanding/service.js +7 -173
  84. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -3
  85. package/dist/src/agent/memory/user-context-coordinator.js +3 -66
  86. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
  87. package/dist/src/agent/service/direct-turn-helpers.js +1 -1
  88. package/dist/src/agent/service/process-direct-streaming.js +4 -1
  89. package/dist/src/agent/service/webchat-tts.d.ts +2 -0
  90. package/dist/src/agent/service/webchat-tts.js +15 -2
  91. package/dist/src/agent/tools/tts-tool.js +19 -13
  92. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  93. package/dist/src/config/schema.d.ts +4 -0
  94. package/dist/src/config/schema.js +1 -0
  95. package/dist/src/connectors/connected-source-understanding.js +35 -21
  96. package/dist/src/connectors/learning-coordinator.js +1 -42
  97. package/dist/src/gateway/chat-stream/mapper.d.ts +2 -0
  98. package/dist/src/gateway/chat-stream/mapper.js +16 -0
  99. package/dist/src/gateway/hono/routes/understanding-sources.js +4 -1
  100. package/dist/src/gateway/hono/routes/voice.js +1 -1
  101. package/dist/src/gateway/hono/routes/you.js +29 -11
  102. package/dist/src/gateway/service/run-gateway-agent.js +5 -1
  103. package/dist/src/knowledge/index.d.ts +0 -1
  104. package/dist/src/knowledge/index.js +1 -2
  105. package/dist/src/notifications/planner.js +5 -1
  106. package/dist/src/storage/sqlite/index.d.ts +1 -1
  107. package/dist/src/storage/sqlite/index.js +2 -2
  108. package/dist/src/storage/sqlite/migrations/137_understanding_status_audit.sql +71 -0
  109. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  110. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  111. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  112. package/dist/src/storage/sqlite/schema.js +1 -1
  113. package/dist/src/storage/sqlite/user-context-repository.d.ts +16 -4
  114. package/dist/src/storage/sqlite/user-context-repository.js +56 -12
  115. package/dist/src/user-context/config.d.ts +8 -0
  116. package/dist/src/user-context/config.js +3 -0
  117. package/dist/src/user-context/consolidation.js +16 -4
  118. package/dist/src/user-context/extraction/executor.d.ts +22 -0
  119. package/dist/src/user-context/extraction/executor.js +142 -0
  120. package/dist/src/user-context/extraction/registry.d.ts +1 -1
  121. package/dist/src/user-context/extraction/registry.js +5 -34
  122. package/dist/src/user-context/extraction/repair.js +4 -1
  123. package/dist/src/user-context/extraction/semantic.d.ts +18 -0
  124. package/dist/src/user-context/extraction/semantic.js +148 -0
  125. package/dist/src/user-context/understandingQuality.js +4 -0
  126. package/dist/src/work-discovery/analyzer.js +9 -4
  127. package/dist/src/work-discovery/service.d.ts +1 -0
  128. package/dist/src/work-discovery/service.js +5 -4
  129. package/dist/src/work-discovery/types.d.ts +1 -0
  130. package/package.json +14 -8
  131. package/dist/gateway/static/root/assets/settings-advanced-gate-Cco_W41x.js +0 -1
  132. package/dist/src/agent/memory/understanding/correction.d.ts +0 -3
  133. package/dist/src/agent/memory/understanding/correction.js +0 -39
  134. package/dist/src/knowledge/connected-understanding-pipeline.d.ts +0 -17
  135. package/dist/src/knowledge/connected-understanding-pipeline.js +0 -278
@@ -4,6 +4,11 @@ import { retrievalQueryAuditValue } from "../../retrieval/audit.js";
4
4
  import { buildFts5SearchQuery, fts5RankToScore } from "./fts.js";
5
5
  import { createHash, randomUUID } from "node:crypto";
6
6
  //#region src/storage/sqlite/user-context-repository.ts
7
+ function insertUnderstandingStatusEvent(db, input) {
8
+ db.prepare(`INSERT INTO understanding_status_events (
9
+ event_id, understanding_id, from_status, to_status, actor_type, source, created_at
10
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), input.understandingId, input.fromStatus ?? null, input.toStatus, input.actorType, input.source, input.createdAt);
11
+ }
7
12
  function evidenceFromRow(row) {
8
13
  return {
9
14
  id: row.evidence_id,
@@ -144,6 +149,13 @@ function createUnderstanding(input, principalId = USER_CONTEXT_PRINCIPAL_ID) {
144
149
  version_id, understanding_id, statement, payload_json, created_by, change_reason, created_at
145
150
  ) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(versionId, id, input.statement.trim(), JSON.stringify(input.payload ?? {}), input.createdBy, input.changeReason, now);
146
151
  db.prepare("INSERT INTO user_understanding_fts (statement, understanding_id, version_id) VALUES (?, ?, ?)").run(input.statement.trim(), id, versionId);
152
+ insertUnderstandingStatusEvent(db, {
153
+ understandingId: id,
154
+ toStatus: input.status,
155
+ actorType: input.createdBy === "user" ? "user" : "runtime",
156
+ source: `create:${input.changeReason}`,
157
+ createdAt: now
158
+ });
147
159
  });
148
160
  return getUnderstanding(id);
149
161
  }
@@ -197,17 +209,29 @@ function reviseUnderstanding(id, statement, options) {
197
209
  });
198
210
  return getUnderstanding(id);
199
211
  }
200
- function setUnderstandingStatus(id, status, confirmation) {
212
+ function setUnderstandingStatus(id, status, options) {
213
+ const current = getUnderstanding(id);
214
+ if (!current) throw new Error(`User understanding not found: ${id}`);
201
215
  const now = Date.now();
202
- getSqliteDatabase().prepare(`UPDATE user_understandings
203
- SET status = ?,
204
- explicitness = COALESCE(?, explicitness),
205
- confidence = COALESCE(?, confidence),
206
- updated_at = ?
207
- WHERE understanding_id = ?`).run(status, confirmation?.explicitness ?? null, confirmation?.confidence ?? null, now, id);
208
- const assertionStatus = status === "active" ? "active" : status === "archived" ? "closed" : status === "rejected" ? "rejected" : "candidate";
209
- getSqliteDatabase().prepare(`UPDATE context_temporal_assertions
210
- SET status = ?, updated_at = ? WHERE object_type = 'understanding' AND object_id = ?`).run(assertionStatus, now, id);
216
+ runSqliteWriteTransaction((db) => {
217
+ db.prepare(`UPDATE user_understandings
218
+ SET status = ?,
219
+ explicitness = COALESCE(?, explicitness),
220
+ confidence = COALESCE(?, confidence),
221
+ updated_at = ?
222
+ WHERE understanding_id = ?`).run(status, options?.explicitness ?? null, options?.confidence ?? null, now, id);
223
+ const assertionStatus = status === "active" ? "active" : status === "archived" ? "closed" : status === "rejected" ? "rejected" : "candidate";
224
+ db.prepare(`UPDATE context_temporal_assertions
225
+ SET status = ?, updated_at = ? WHERE object_type = 'understanding' AND object_id = ?`).run(assertionStatus, now, id);
226
+ if (current.status !== status) insertUnderstandingStatusEvent(db, {
227
+ understandingId: id,
228
+ fromStatus: current.status,
229
+ toStatus: status,
230
+ actorType: options?.actorType ?? "runtime",
231
+ source: options?.source ?? "repository-status-change",
232
+ createdAt: now
233
+ });
234
+ });
211
235
  const result = getUnderstanding(id);
212
236
  if (!result) throw new Error(`User understanding not found: ${id}`);
213
237
  return result;
@@ -219,12 +243,20 @@ function closeUnderstandingValidity(id, validTo = Date.now()) {
219
243
  if (!result) throw new Error(`User understanding not found: ${id}`);
220
244
  return result;
221
245
  }
222
- function rejectUnderstanding(id, reason) {
246
+ function rejectUnderstanding(id, reason, actorType = "runtime") {
223
247
  const current = getUnderstanding(id);
224
248
  if (!current) throw new Error(`User understanding not found: ${id}`);
225
249
  const now = Date.now();
226
250
  runSqliteWriteTransaction((db) => {
227
251
  db.prepare("UPDATE user_understandings SET status = 'rejected', updated_at = ? WHERE understanding_id = ?").run(now, id);
252
+ if (current.status !== "rejected") insertUnderstandingStatusEvent(db, {
253
+ understandingId: id,
254
+ fromStatus: current.status,
255
+ toStatus: "rejected",
256
+ actorType,
257
+ source: reason,
258
+ createdAt: now
259
+ });
228
260
  db.prepare(`INSERT INTO context_suppressions (
229
261
  suppression_id, principal_id, canonical_key, scope_type, scope_id, reason, expires_at, created_at
230
262
  ) VALUES (?, ?, ?, ?, ?, ?, NULL, ?)
@@ -232,6 +264,18 @@ function rejectUnderstanding(id, reason) {
232
264
  });
233
265
  return getUnderstanding(id);
234
266
  }
267
+ function listUnderstandingStatusEvents(id) {
268
+ return getSqliteDatabase().prepare(`SELECT * FROM understanding_status_events
269
+ WHERE understanding_id = ? ORDER BY created_at DESC, rowid DESC`).all(id).map((row) => ({
270
+ id: row.event_id,
271
+ understandingId: row.understanding_id,
272
+ ...row.from_status ? { fromStatus: row.from_status } : {},
273
+ toStatus: row.to_status,
274
+ actorType: row.actor_type,
275
+ source: row.source,
276
+ createdAt: row.created_at
277
+ }));
278
+ }
235
279
  function deleteUnderstanding(id) {
236
280
  return runSqliteWriteTransaction((db) => {
237
281
  db.prepare("DELETE FROM user_understanding_fts WHERE understanding_id = ?").run(id);
@@ -442,4 +486,4 @@ function decideContextConsent(id, decision) {
442
486
  return Number(result.changes) > 0;
443
487
  }
444
488
  //#endregion
445
- export { closeUnderstandingValidity, consumeContextConsent, createCollaborationRule, createContextEvidence, createUnderstanding, decideContextConsent, deleteCollaborationRule, deleteUnderstanding, ensureContextConsent, getCollaborationRule, getTurnPersonalization, getUnderstanding, getUserProfile, hasContextConsent, isUnderstandingSuppressed, linkUnderstandingEvidence, listCollaborationRules, listUnderstandingEvidence, listUnderstandings, recordContextFeedback, recordContextRun, rejectUnderstanding, reviseCollaborationRule, reviseUnderstanding, searchActiveUnderstandings, setCollaborationRuleStatus, setUnderstandingStatus, updateUserProfile };
489
+ export { closeUnderstandingValidity, consumeContextConsent, createCollaborationRule, createContextEvidence, createUnderstanding, decideContextConsent, deleteCollaborationRule, deleteUnderstanding, ensureContextConsent, getCollaborationRule, getTurnPersonalization, getUnderstanding, getUserProfile, hasContextConsent, isUnderstandingSuppressed, linkUnderstandingEvidence, listCollaborationRules, listUnderstandingEvidence, listUnderstandingStatusEvents, listUnderstandings, recordContextFeedback, recordContextRun, rejectUnderstanding, reviseCollaborationRule, reviseUnderstanding, searchActiveUnderstandings, setCollaborationRuleStatus, setUnderstandingStatus, updateUserProfile };
@@ -96,6 +96,10 @@ export declare const UserMemoryConfigSchema: z.ZodDefault<z.ZodObject<{
96
96
  }, z.core.$strict>>;
97
97
  export declare const UserUnderstandingConfigSchema: z.ZodDefault<z.ZodObject<{
98
98
  enabled: z.ZodDefault<z.ZodBoolean>;
99
+ processingPolicy: z.ZodDefault<z.ZodEnum<{
100
+ local_only: "local_only";
101
+ remote_allowed: "remote_allowed";
102
+ }>>;
99
103
  adaptiveCadence: z.ZodDefault<z.ZodBoolean>;
100
104
  reviewIntervalTurns: z.ZodDefault<z.ZodNumber>;
101
105
  maxHistoryMessages: z.ZodDefault<z.ZodNumber>;
@@ -204,6 +208,10 @@ export declare const UserContextConfigSchema: z.ZodDefault<z.ZodObject<{
204
208
  }, z.core.$strict>>;
205
209
  understanding: z.ZodDefault<z.ZodObject<{
206
210
  enabled: z.ZodDefault<z.ZodBoolean>;
211
+ processingPolicy: z.ZodDefault<z.ZodEnum<{
212
+ local_only: "local_only";
213
+ remote_allowed: "remote_allowed";
214
+ }>>;
207
215
  adaptiveCadence: z.ZodDefault<z.ZodBoolean>;
208
216
  reviewIntervalTurns: z.ZodDefault<z.ZodNumber>;
209
217
  maxHistoryMessages: z.ZodDefault<z.ZodNumber>;
@@ -91,12 +91,14 @@ var init_config = __esmMin((() => {
91
91
  });
92
92
  UserUnderstandingConfigSchema = z.object({
93
93
  enabled: z.boolean().default(true),
94
+ processingPolicy: z.enum(["local_only", "remote_allowed"]).default("remote_allowed"),
94
95
  adaptiveCadence: z.boolean().default(true),
95
96
  reviewIntervalTurns: z.number().int().min(1).max(1e3).default(10),
96
97
  maxHistoryMessages: z.number().int().min(1).max(200).default(80),
97
98
  maxDurationMs: z.number().int().min(1e3).max(6e5).default(12e4)
98
99
  }).strict().default({
99
100
  enabled: true,
101
+ processingPolicy: "remote_allowed",
100
102
  adaptiveCadence: true,
101
103
  reviewIntervalTurns: 10,
102
104
  maxHistoryMessages: 80,
@@ -161,6 +163,7 @@ var init_config = __esmMin((() => {
161
163
  },
162
164
  understanding: {
163
165
  enabled: true,
166
+ processingPolicy: "remote_allowed",
164
167
  adaptiveCadence: true,
165
168
  reviewIntervalTurns: 10,
166
169
  maxHistoryMessages: 80,
@@ -97,7 +97,10 @@ async function runContextConsolidation(input) {
97
97
  const evidenceCount = distinctEvidenceCount(understanding.id, "supports");
98
98
  const contradictionCount = distinctEvidenceCount(understanding.id, "contradicts");
99
99
  if (understanding.validTo != null && understanding.validTo <= now || understanding.expiresAt != null && understanding.expiresAt <= now) {
100
- setUnderstandingStatus(understanding.id, "stale");
100
+ setUnderstandingStatus(understanding.id, "stale", {
101
+ actorType: "runtime",
102
+ source: "consolidation-expired"
103
+ });
101
104
  recordContextConsolidationDecision({
102
105
  runId: run.runId,
103
106
  understandingId: understanding.id,
@@ -110,7 +113,10 @@ async function runContextConsolidation(input) {
110
113
  continue;
111
114
  }
112
115
  if (contradictionCount > 0) {
113
- setUnderstandingStatus(understanding.id, "needs_review");
116
+ setUnderstandingStatus(understanding.id, "needs_review", {
117
+ actorType: "runtime",
118
+ source: "consolidation-contradiction"
119
+ });
114
120
  recordContextConsolidationDecision({
115
121
  runId: run.runId,
116
122
  understandingId: understanding.id,
@@ -123,7 +129,10 @@ async function runContextConsolidation(input) {
123
129
  continue;
124
130
  }
125
131
  if (understanding.status === "active" && understanding.reviewAt != null && understanding.reviewAt <= now) {
126
- setUnderstandingStatus(understanding.id, "needs_review");
132
+ setUnderstandingStatus(understanding.id, "needs_review", {
133
+ actorType: "runtime",
134
+ source: "consolidation-review-due"
135
+ });
127
136
  recordContextConsolidationDecision({
128
137
  runId: run.runId,
129
138
  understandingId: understanding.id,
@@ -136,7 +145,10 @@ async function runContextConsolidation(input) {
136
145
  continue;
137
146
  }
138
147
  if (understanding.status === "candidate" && evidenceCount >= resolved.minEvidenceSources) {
139
- setUnderstandingStatus(understanding.id, "needs_review");
148
+ setUnderstandingStatus(understanding.id, "needs_review", {
149
+ actorType: "runtime",
150
+ source: "consolidation-evidence-threshold"
151
+ });
140
152
  recordContextConsolidationDecision({
141
153
  runId: run.runId,
142
154
  understandingId: understanding.id,
@@ -0,0 +1,22 @@
1
+ import type { Config } from '../../config/schema.js';
2
+ import type { MemoryManager } from '../../agent/memory/manager.js';
3
+ import type { UnderstandingReviewResult } from '../../agent/memory/understanding/types.js';
4
+ import { type ExtractorId } from './registry.js';
5
+ import type { SemanticEvidence, SemanticUnderstandingInterpretation } from './semantic.js';
6
+ export type UnderstandingEvidenceMessage = SemanticEvidence & {
7
+ createdAt: number;
8
+ message: unknown;
9
+ };
10
+ export declare function emptyUnderstandingReview(): UnderstandingReviewResult;
11
+ export declare function executeUnderstandingInterpretation(params: {
12
+ interpretation: SemanticUnderstandingInterpretation;
13
+ evidence: UnderstandingEvidenceMessage[];
14
+ extractionRunId: string;
15
+ extractorId: ExtractorId;
16
+ sessionKey: string;
17
+ turnId?: string;
18
+ memoryManager: MemoryManager;
19
+ getConfig: () => Config | undefined;
20
+ reviewSource: 'turn' | 'background';
21
+ processingPolicy: 'local_only' | 'remote_allowed';
22
+ }): Promise<UnderstandingReviewResult>;
@@ -0,0 +1,142 @@
1
+ import { createContextEvidence, getTurnPersonalization, getUnderstanding, recordContextFeedback, rejectUnderstanding, setUnderstandingStatus } from "../../storage/sqlite/user-context-repository.js";
2
+ import { getSessionMetadata } from "../../storage/sqlite/session-repository.js";
3
+ import "../../storage/sqlite/index.js";
4
+ import { extractionInputHash } from "./registry.js";
5
+ //#region src/user-context/extraction/executor.ts
6
+ function emptyUnderstandingReview() {
7
+ return {
8
+ proposed: 0,
9
+ created: 0,
10
+ deduplicated: 0,
11
+ rejected: 0,
12
+ createdRecords: [],
13
+ writeOutputs: []
14
+ };
15
+ }
16
+ function resolveWritePolicy(config) {
17
+ const memory = config?.userContext.memory;
18
+ if (!memory || memory.mode === "off" || memory.mode === "readOnly") return "deny";
19
+ return memory.writePolicy?.understanding ?? (memory.mode === "auto" ? "allow" : "confirm");
20
+ }
21
+ function recordTargetFeedback(turnId, targetIds, rating, reason) {
22
+ if (!turnId || targetIds.length === 0) return;
23
+ const personalization = getTurnPersonalization(turnId);
24
+ if (!personalization) return;
25
+ for (const objectId of targetIds) recordContextFeedback({
26
+ turnId,
27
+ runId: personalization.runId,
28
+ objectType: "understanding",
29
+ objectId,
30
+ rating,
31
+ reason
32
+ });
33
+ }
34
+ async function executeUnderstandingInterpretation(params) {
35
+ const { interpretation } = params;
36
+ const result = emptyUnderstandingReview();
37
+ const targets = interpretation.targetUnderstandingIds.flatMap((id) => {
38
+ const item = getUnderstanding(id);
39
+ return item ? [item] : [];
40
+ });
41
+ const targetIds = targets.map((item) => item.id);
42
+ const policy = resolveWritePolicy(params.getConfig());
43
+ if (interpretation.intent === "memory_forget") {
44
+ for (const target of targets) {
45
+ const rejected = rejectUnderstanding(target.id, "explicit user forget request", "user");
46
+ result.writeOutputs.push({
47
+ candidateKey: rejected.canonicalKey,
48
+ objectId: rejected.id,
49
+ versionId: rejected.versionId,
50
+ outcome: "rejected"
51
+ });
52
+ }
53
+ result.rejected += targets.length;
54
+ recordTargetFeedback(params.turnId, targetIds, "irrelevant", "explicit_user_forget_request");
55
+ return result;
56
+ }
57
+ if (interpretation.intent === "memory_confirm" && policy !== "deny") {
58
+ for (const target of targets) {
59
+ const confirmed = setUnderstandingStatus(target.id, "active", {
60
+ explicitness: "explicit",
61
+ confidence: 1,
62
+ actorType: "user",
63
+ source: "semantic-memory-confirmation"
64
+ });
65
+ result.createdRecords.push({
66
+ id: confirmed.id,
67
+ content: confirmed.statement,
68
+ kind: confirmed.kind,
69
+ status: confirmed.status
70
+ });
71
+ result.writeOutputs.push({
72
+ candidateKey: confirmed.canonicalKey,
73
+ objectId: confirmed.id,
74
+ versionId: confirmed.versionId,
75
+ outcome: "deduplicated"
76
+ });
77
+ result.deduplicated += 1;
78
+ }
79
+ recordTargetFeedback(params.turnId, targetIds, "helpful", "explicit_user_confirmation");
80
+ }
81
+ if (interpretation.intent === "memory_correct" && targets.length && interpretation.candidates.length === 0) {
82
+ for (const target of targets) setUnderstandingStatus(target.id, "needs_review", {
83
+ actorType: "user",
84
+ source: "semantic-memory-correction"
85
+ });
86
+ recordTargetFeedback(params.turnId, targetIds, "wrong", "explicit_user_correction_without_replacement");
87
+ return result;
88
+ }
89
+ if (interpretation.candidates.length === 0) return result;
90
+ if (policy === "deny") {
91
+ result.proposed = interpretation.candidates.length;
92
+ result.rejected = interpretation.candidates.length;
93
+ return result;
94
+ }
95
+ const evidenceByRef = new Map(params.evidence.map((entry) => [entry.ref, entry]));
96
+ const candidates = interpretation.candidates.map((candidate) => ({
97
+ ...candidate,
98
+ explicitness: policy === "confirm" && candidate.explicitness === "explicit" ? "observed" : candidate.explicitness
99
+ }));
100
+ const evidenceIdsByRef = /* @__PURE__ */ new Map();
101
+ for (const ref of new Set(candidates.flatMap((candidate) => candidate.evidenceRefs))) {
102
+ const entry = evidenceByRef.get(ref);
103
+ if (!entry || entry.role !== "user") continue;
104
+ const evidence = createContextEvidence({
105
+ sourceType: "conversation",
106
+ sourceRef: `session:${params.sessionKey}:entry:${entry.ref}`,
107
+ sourceRunId: params.extractionRunId,
108
+ sessionId: params.sessionKey,
109
+ ...params.turnId ? { turnId: params.turnId } : {},
110
+ messageId: entry.ref,
111
+ contentHash: extractionInputHash(JSON.stringify(entry.message)),
112
+ retentionPolicy: "derived_only",
113
+ processingPolicy: params.processingPolicy,
114
+ extractorId: params.extractorId,
115
+ extractorVersion: "1",
116
+ trustLevel: "owner",
117
+ observedAt: entry.createdAt
118
+ });
119
+ evidenceIdsByRef.set(ref, evidence.id);
120
+ }
121
+ const projectId = getSessionMetadata(params.sessionKey)?.projectId;
122
+ for (const candidate of candidates) {
123
+ const applied = await params.memoryManager.applyUnderstandingCandidates([candidate], {
124
+ sessionKey: params.sessionKey,
125
+ ...projectId ? { projectId } : {},
126
+ evidenceIds: candidate.evidenceRefs.flatMap((ref) => evidenceIdsByRef.get(ref) ?? []),
127
+ reviewSource: params.reviewSource,
128
+ extractionRunId: params.extractionRunId,
129
+ ...interpretation.intent === "memory_correct" ? { supersedesRecordIds: targetIds } : {}
130
+ });
131
+ result.proposed += applied.proposed;
132
+ result.created += applied.created;
133
+ result.deduplicated += applied.deduplicated;
134
+ result.rejected += applied.rejected;
135
+ result.createdRecords.push(...applied.createdRecords);
136
+ result.writeOutputs.push(...applied.writeOutputs ?? []);
137
+ }
138
+ if (interpretation.intent === "memory_correct" && result.created > 0) recordTargetFeedback(params.turnId, targetIds, "wrong", "explicit_user_correction_with_replacement");
139
+ return result;
140
+ }
141
+ //#endregion
142
+ export { emptyUnderstandingReview, executeUnderstandingInterpretation };
@@ -1,6 +1,6 @@
1
1
  import { type ContextExtractionRun } from '../../storage/sqlite/index.js';
2
2
  import type { UnderstandingKind } from '../domain.js';
3
- export type ExtractorId = 'explicit-command' | 'deterministic-signal' | 'transcript-synthesis' | 'connector-structural' | 'connector-semantic';
3
+ export type ExtractorId = 'turn-semantics' | 'transcript-synthesis' | 'connector-semantic';
4
4
  export type ExtractorDefinition = {
5
5
  id: ExtractorId;
6
6
  version: string;
@@ -14,62 +14,33 @@ const UNDERSTANDING_KINDS = [
14
14
  "derived_insight"
15
15
  ];
16
16
  const DEFINITIONS = {
17
- "explicit-command": {
18
- id: "explicit-command",
17
+ "turn-semantics": {
18
+ id: "turn-semantics",
19
19
  version: "1",
20
20
  inputKinds: ["conversation"],
21
21
  requiredProcessingPolicy: "local_only",
22
22
  authorityCeiling: "user_explicit",
23
23
  candidateKinds: UNDERSTANDING_KINDS,
24
24
  maxAutomaticStatus: "active",
25
- timeoutMs: 500
26
- },
27
- "deterministic-signal": {
28
- id: "deterministic-signal",
29
- version: "1",
30
- inputKinds: ["conversation", "task"],
31
- requiredProcessingPolicy: "local_only",
32
- authorityCeiling: "user_observed",
33
- candidateKinds: UNDERSTANDING_KINDS,
34
- maxAutomaticStatus: "candidate",
35
- timeoutMs: 500
25
+ timeoutMs: 3e4
36
26
  },
37
27
  "transcript-synthesis": {
38
28
  id: "transcript-synthesis",
39
29
  version: "1",
40
30
  inputKinds: ["conversation"],
41
- requiredProcessingPolicy: "remote_allowed",
31
+ requiredProcessingPolicy: "local_only",
42
32
  authorityCeiling: "system_inferred",
43
33
  candidateKinds: UNDERSTANDING_KINDS,
44
34
  maxAutomaticStatus: "candidate",
45
35
  timeoutMs: 3e4
46
36
  },
47
- "connector-structural": {
48
- id: "connector-structural",
49
- version: "1",
50
- inputKinds: ["connector"],
51
- requiredProcessingPolicy: "local_only",
52
- authorityCeiling: "external_untrusted",
53
- candidateKinds: [
54
- "project_context",
55
- "current_state",
56
- "routine"
57
- ],
58
- maxAutomaticStatus: "candidate",
59
- timeoutMs: 1e4
60
- },
61
37
  "connector-semantic": {
62
38
  id: "connector-semantic",
63
39
  version: "1",
64
40
  inputKinds: ["connector", "file"],
65
41
  requiredProcessingPolicy: "remote_allowed",
66
42
  authorityCeiling: "external_untrusted",
67
- candidateKinds: [
68
- "preference",
69
- "routine",
70
- "current_state",
71
- "project_context"
72
- ],
43
+ candidateKinds: ["preference", "routine"],
73
44
  maxAutomaticStatus: "candidate",
74
45
  timeoutMs: 3e4
75
46
  }
@@ -39,7 +39,10 @@ function repairExtractedContext(filter) {
39
39
  });
40
40
  continue;
41
41
  }
42
- setUnderstandingStatus(output.objectId, "archived");
42
+ setUnderstandingStatus(output.objectId, "archived", {
43
+ actorType: "runtime",
44
+ source: "context-extraction-repair"
45
+ });
43
46
  } else {
44
47
  const focus = listUserFocuses().find((item) => item.id === output.objectId);
45
48
  if (!focus) continue;
@@ -0,0 +1,18 @@
1
+ import type { UnderstandingCandidate } from '../../agent/memory/understanding/types.js';
2
+ export declare const UNDERSTANDING_INTENTS: readonly ["memory_create", "memory_query", "memory_confirm", "memory_correct", "memory_forget", "user_assertion", "task_request", "none"];
3
+ export type UnderstandingIntent = typeof UNDERSTANDING_INTENTS[number];
4
+ export type SemanticEvidence = {
5
+ ref: string;
6
+ role: 'user' | 'assistant';
7
+ text: string;
8
+ };
9
+ export type SemanticUnderstandingCandidate = UnderstandingCandidate & {
10
+ evidenceRefs: string[];
11
+ };
12
+ export type SemanticUnderstandingInterpretation = {
13
+ intent: UnderstandingIntent;
14
+ candidates: SemanticUnderstandingCandidate[];
15
+ targetUnderstandingIds: string[];
16
+ abstentionReason?: string;
17
+ };
18
+ export declare function parseSemanticUnderstanding(raw: string, evidence: SemanticEvidence[], allowedTargetIds?: string[]): SemanticUnderstandingInterpretation | null;
@@ -0,0 +1,148 @@
1
+ import { UNDERSTANDING_KINDS } from "../domain.js";
2
+ import { isDurableUnderstandingCandidate } from "../understandingQuality.js";
3
+ import { inferMemorySensitivity } from "../../agent/memory/sensitivity.js";
4
+ import { z } from "zod";
5
+ //#region src/user-context/extraction/semantic.ts
6
+ const UNDERSTANDING_INTENTS = [
7
+ "memory_create",
8
+ "memory_query",
9
+ "memory_confirm",
10
+ "memory_correct",
11
+ "memory_forget",
12
+ "user_assertion",
13
+ "task_request",
14
+ "none"
15
+ ];
16
+ const EvidenceSchema = z.object({
17
+ ref: z.string().min(1),
18
+ quote: z.string().min(1)
19
+ }).strict();
20
+ const CandidateSchema = z.object({
21
+ factKey: z.string().regex(/^[a-z0-9][a-z0-9:-]{2,119}$/),
22
+ statement: z.string().min(4).max(600),
23
+ kind: z.enum(UNDERSTANDING_KINDS),
24
+ explicitness: z.enum([
25
+ "explicit",
26
+ "observed",
27
+ "inferred"
28
+ ]),
29
+ durability: z.enum([
30
+ "ephemeral",
31
+ "durable",
32
+ "recurring"
33
+ ]),
34
+ scopeHint: z.enum([
35
+ "global",
36
+ "workspace",
37
+ "project",
38
+ "session"
39
+ ]),
40
+ confidence: z.number().min(0).max(1),
41
+ importance: z.number().min(0).max(1),
42
+ sensitivity: z.enum([
43
+ "normal",
44
+ "personal",
45
+ "secret",
46
+ "regulated"
47
+ ]).optional(),
48
+ disclosurePolicy: z.enum([
49
+ "silent",
50
+ "referenceable",
51
+ "ask_before_reference"
52
+ ]).optional(),
53
+ evidence: z.array(EvidenceSchema).min(1).max(8),
54
+ selfContained: z.boolean(),
55
+ unresolvedReferences: z.array(z.string()).max(8)
56
+ }).strict();
57
+ const InterpretationSchema = z.object({
58
+ intent: z.enum(UNDERSTANDING_INTENTS),
59
+ candidates: z.array(CandidateSchema).max(8),
60
+ targetUnderstandingIds: z.array(z.string().min(1)).max(8),
61
+ abstentionReason: z.string().max(500).optional()
62
+ }).strict();
63
+ const CANDIDATE_INTENTS = new Set([
64
+ "memory_create",
65
+ "memory_confirm",
66
+ "memory_correct",
67
+ "user_assertion"
68
+ ]);
69
+ const QUESTION_END = /[??]\s*$/u;
70
+ function extractJson(raw) {
71
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(raw)?.[1];
72
+ const start = raw.indexOf("{");
73
+ const end = raw.lastIndexOf("}");
74
+ const candidate = fenced ?? (start >= 0 && end > start ? raw.slice(start, end + 1) : raw);
75
+ return JSON.parse(candidate);
76
+ }
77
+ function comparableText(value) {
78
+ return value.normalize("NFKC").toLocaleLowerCase().replace(/\s+/g, " ").trim();
79
+ }
80
+ function quoteIsGrounded(quote, source) {
81
+ return comparableText(source).includes(comparableText(quote));
82
+ }
83
+ function bigramSimilarity(left, right) {
84
+ const pairs = (value) => {
85
+ const normalized = comparableText(value).replace(/[^\p{L}\p{N}]/gu, "");
86
+ const output = /* @__PURE__ */ new Set();
87
+ for (let index = 0; index < normalized.length - 1; index += 1) output.add(normalized.slice(index, index + 2));
88
+ return output;
89
+ };
90
+ const a = pairs(left);
91
+ const b = pairs(right);
92
+ if (!a.size || !b.size) return 0;
93
+ let overlap = 0;
94
+ for (const pair of a) if (b.has(pair)) overlap += 1;
95
+ return 2 * overlap / (a.size + b.size);
96
+ }
97
+ function effectiveExplicitness(intent, declared) {
98
+ if (intent === "memory_create" || intent === "memory_correct" || intent === "memory_confirm") return declared;
99
+ return declared === "explicit" ? "observed" : declared;
100
+ }
101
+ function parseSemanticUnderstanding(raw, evidence, allowedTargetIds = []) {
102
+ let parsed;
103
+ try {
104
+ const result = InterpretationSchema.safeParse(extractJson(raw));
105
+ if (!result.success) return null;
106
+ parsed = result.data;
107
+ } catch {
108
+ return null;
109
+ }
110
+ const evidenceByRef = new Map(evidence.map((item) => [item.ref, item]));
111
+ const allowedTargets = new Set(allowedTargetIds);
112
+ const candidates = [];
113
+ if (CANDIDATE_INTENTS.has(parsed.intent)) for (const item of parsed.candidates) {
114
+ if (!item.selfContained || item.unresolvedReferences.length > 0) continue;
115
+ if (QUESTION_END.test(item.statement)) continue;
116
+ if (!isDurableUnderstandingCandidate(item.kind, item.statement)) continue;
117
+ if (!item.evidence.every((claim) => {
118
+ const source = evidenceByRef.get(claim.ref);
119
+ return source?.role === "user" && quoteIsGrounded(claim.quote, source.text);
120
+ })) continue;
121
+ if (item.explicitness === "explicit") {
122
+ const quotedText = item.evidence.map((claim) => claim.quote).join(" ");
123
+ if (bigramSimilarity(item.statement, quotedText) < .45) continue;
124
+ }
125
+ const evidenceRefs = [...new Set(item.evidence.map((claim) => claim.ref))];
126
+ candidates.push({
127
+ kind: item.kind,
128
+ content: item.statement.trim(),
129
+ canonicalKey: `understanding:${item.kind}:${item.factKey}`,
130
+ confidence: item.confidence,
131
+ importance: item.importance,
132
+ explicitness: effectiveExplicitness(parsed.intent, item.explicitness),
133
+ durability: item.durability,
134
+ sensitivity: item.sensitivity ?? inferMemorySensitivity(item.statement),
135
+ disclosurePolicy: item.disclosurePolicy ?? "referenceable",
136
+ payload: { scopeHint: item.scopeHint },
137
+ evidenceRefs
138
+ });
139
+ }
140
+ return {
141
+ intent: parsed.intent,
142
+ candidates,
143
+ targetUnderstandingIds: [...new Set(parsed.targetUnderstandingIds.filter((id) => allowedTargets.has(id)))],
144
+ ...parsed.abstentionReason ? { abstentionReason: parsed.abstentionReason } : {}
145
+ };
146
+ }
147
+ //#endregion
148
+ export { UNDERSTANDING_INTENTS, parseSemanticUnderstanding };
@@ -2,6 +2,8 @@
2
2
  const INCOMPLETE_PREFIX = /^(?:and\b|also\b|then\b|plus\b|as\s+well\s+as\b|which\b|that\b|并且|并将|以及|然后|同时|另外|还有|而且|或者|的事项|事项[,,、::]?)/i;
3
3
  const DEFERRED_OR_NEGATED_ACTION = /(?:not\s+now|no\s+need\s+to|do\s+not\s+(?:start|proceed)|don't\s+(?:start|proceed)|later\s+maybe|暂时不用|暂时不(?:用|要|开始)?|先不用|无需|不用开始|不要推进|不需要推进)/i;
4
4
  const ONE_OFF_ACTION_PREFIX = /^(?:please\s+|update\b|investigate\b|summari[sz]e\b|fix\b|write\b|add\b|record\b|track\b)|^(?:请|帮我|更新|调查|汇总|整理|修复|写入|加入|记录|跟进)/i;
5
+ const DURABLE_MARKER = /(?:从现在起|今后|以后|长期|每次|每回|始终|一直|默认|下次|教训|原则|习惯|from now on|going forward|long[- ]term|every time|always|by default|next time|lesson|principle|habit)/i;
6
+ const TURN_EXECUTION_INSTRUCTION = /(?:不要再问|别再问|直接执行|立即执行|当前任务|这次|本次|现在就|do not ask|don't ask|just do it|execute directly|proceed now|this task|this time)/i;
5
7
  function meaningfulLength(content) {
6
8
  return content.normalize("NFKC").replace(/[\p{P}\p{S}\s]/gu, "").length;
7
9
  }
@@ -15,6 +17,8 @@ function isActionableUnderstandingContent(content) {
15
17
  function isDurableUnderstandingCandidate(kind, content) {
16
18
  if (!isStandaloneUnderstandingContent(content)) return false;
17
19
  if ((kind === "commitment" || kind === "long_term_goal") && ONE_OFF_ACTION_PREFIX.test(content.trim())) return false;
20
+ if (kind === "task_lesson" && !DURABLE_MARKER.test(content)) return false;
21
+ if (kind === "boundary" && TURN_EXECUTION_INSTRUCTION.test(content) && !DURABLE_MARKER.test(content)) return false;
18
22
  return true;
19
23
  }
20
24
  function comparableText(content) {