crewly 1.20.183 → 1.20.184

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 (128) hide show
  1. package/dist/backend/backend/src/constants.d.ts +44 -0
  2. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  3. package/dist/backend/backend/src/constants.js +44 -0
  4. package/dist/backend/backend/src/constants.js.map +1 -1
  5. package/dist/backend/backend/src/controllers/request/open-items.controller.d.ts +25 -0
  6. package/dist/backend/backend/src/controllers/request/open-items.controller.d.ts.map +1 -0
  7. package/dist/backend/backend/src/controllers/request/open-items.controller.js +68 -0
  8. package/dist/backend/backend/src/controllers/request/open-items.controller.js.map +1 -0
  9. package/dist/backend/backend/src/controllers/request/request.routes.d.ts +2 -0
  10. package/dist/backend/backend/src/controllers/request/request.routes.d.ts.map +1 -1
  11. package/dist/backend/backend/src/controllers/request/request.routes.js +6 -0
  12. package/dist/backend/backend/src/controllers/request/request.routes.js.map +1 -1
  13. package/dist/backend/backend/src/index.d.ts.map +1 -1
  14. package/dist/backend/backend/src/index.js +57 -1
  15. package/dist/backend/backend/src/index.js.map +1 -1
  16. package/dist/backend/backend/src/scripts/open-items-backfill.d.ts +16 -0
  17. package/dist/backend/backend/src/scripts/open-items-backfill.d.ts.map +1 -0
  18. package/dist/backend/backend/src/scripts/open-items-backfill.js +112 -0
  19. package/dist/backend/backend/src/scripts/open-items-backfill.js.map +1 -0
  20. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  21. package/dist/backend/backend/src/services/agent/agent-registration.service.js +4 -2
  22. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  23. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +9 -0
  24. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +11 -0
  26. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  27. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +10 -0
  28. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  29. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +20 -0
  30. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  31. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +8 -1
  32. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  33. package/dist/backend/backend/src/services/decisions/decision.service.js +12 -2
  34. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  35. package/dist/backend/backend/src/services/open-items/open-item-card.d.ts +66 -0
  36. package/dist/backend/backend/src/services/open-items/open-item-card.d.ts.map +1 -0
  37. package/dist/backend/backend/src/services/open-items/open-item-card.js +208 -0
  38. package/dist/backend/backend/src/services/open-items/open-item-card.js.map +1 -0
  39. package/dist/backend/backend/src/services/open-items/open-item-extractor.d.ts +114 -0
  40. package/dist/backend/backend/src/services/open-items/open-item-extractor.d.ts.map +1 -0
  41. package/dist/backend/backend/src/services/open-items/open-item-extractor.js +394 -0
  42. package/dist/backend/backend/src/services/open-items/open-item-extractor.js.map +1 -0
  43. package/dist/backend/backend/src/services/open-items/open-items-backfill.d.ts +70 -0
  44. package/dist/backend/backend/src/services/open-items/open-items-backfill.d.ts.map +1 -0
  45. package/dist/backend/backend/src/services/open-items/open-items-backfill.js +147 -0
  46. package/dist/backend/backend/src/services/open-items/open-items-backfill.js.map +1 -0
  47. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts +332 -0
  48. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -0
  49. package/dist/backend/backend/src/services/open-items/open-items.service.js +785 -0
  50. package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -0
  51. package/dist/backend/backend/src/services/open-items/open-items.wiring.d.ts +44 -0
  52. package/dist/backend/backend/src/services/open-items/open-items.wiring.d.ts.map +1 -0
  53. package/dist/backend/backend/src/services/open-items/open-items.wiring.js +172 -0
  54. package/dist/backend/backend/src/services/open-items/open-items.wiring.js.map +1 -0
  55. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +4 -0
  57. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  58. package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
  59. package/dist/backend/backend/src/services/v3/request.service.js +15 -1
  60. package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
  61. package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
  62. package/dist/backend/backend/src/services/v3/ticket-review.service.js +5 -1
  63. package/dist/backend/backend/src/services/v3/ticket-review.service.js.map +1 -1
  64. package/dist/backend/backend/src/types/decision.types.d.ts +15 -1
  65. package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
  66. package/dist/backend/backend/src/types/v2/index.d.ts +2 -0
  67. package/dist/backend/backend/src/types/v2/index.d.ts.map +1 -1
  68. package/dist/backend/backend/src/types/v2/index.js +1 -0
  69. package/dist/backend/backend/src/types/v2/index.js.map +1 -1
  70. package/dist/backend/backend/src/types/v2/open-item.types.d.ts +73 -0
  71. package/dist/backend/backend/src/types/v2/open-item.types.d.ts.map +1 -0
  72. package/dist/backend/backend/src/types/v2/open-item.types.js +14 -0
  73. package/dist/backend/backend/src/types/v2/open-item.types.js.map +1 -0
  74. package/dist/backend/backend/src/types/v2/request.types.d.ts +22 -1
  75. package/dist/backend/backend/src/types/v2/request.types.d.ts.map +1 -1
  76. package/dist/backend/backend/src/types/v2/request.types.js +10 -5
  77. package/dist/backend/backend/src/types/v2/request.types.js.map +1 -1
  78. package/dist/backend/build-info.json +2 -2
  79. package/dist/cli/backend/src/constants.d.ts +44 -0
  80. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  81. package/dist/cli/backend/src/constants.js +44 -0
  82. package/dist/cli/backend/src/constants.js.map +1 -1
  83. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  84. package/dist/cli/backend/src/services/agent/agent-registration.service.js +4 -2
  85. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  86. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +9 -0
  87. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  88. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +11 -0
  89. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  90. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts +10 -0
  91. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  92. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js +20 -0
  93. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  94. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +8 -1
  95. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  96. package/dist/cli/backend/src/services/decisions/decision.service.js +12 -2
  97. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  98. package/dist/cli/backend/src/services/open-items/open-item-card.d.ts +66 -0
  99. package/dist/cli/backend/src/services/open-items/open-item-card.d.ts.map +1 -0
  100. package/dist/cli/backend/src/services/open-items/open-item-card.js +208 -0
  101. package/dist/cli/backend/src/services/open-items/open-item-card.js.map +1 -0
  102. package/dist/cli/backend/src/services/open-items/open-item-extractor.d.ts +114 -0
  103. package/dist/cli/backend/src/services/open-items/open-item-extractor.d.ts.map +1 -0
  104. package/dist/cli/backend/src/services/open-items/open-item-extractor.js +394 -0
  105. package/dist/cli/backend/src/services/open-items/open-item-extractor.js.map +1 -0
  106. package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
  107. package/dist/cli/backend/src/services/v3/request.service.js +15 -1
  108. package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
  109. package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
  110. package/dist/cli/backend/src/services/v3/ticket-review.service.js +5 -1
  111. package/dist/cli/backend/src/services/v3/ticket-review.service.js.map +1 -1
  112. package/dist/cli/backend/src/types/decision.types.d.ts +15 -1
  113. package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
  114. package/dist/cli/backend/src/types/v2/index.d.ts +2 -0
  115. package/dist/cli/backend/src/types/v2/index.d.ts.map +1 -1
  116. package/dist/cli/backend/src/types/v2/index.js +1 -0
  117. package/dist/cli/backend/src/types/v2/index.js.map +1 -1
  118. package/dist/cli/backend/src/types/v2/open-item.types.d.ts +73 -0
  119. package/dist/cli/backend/src/types/v2/open-item.types.d.ts.map +1 -0
  120. package/dist/cli/backend/src/types/v2/open-item.types.js +14 -0
  121. package/dist/cli/backend/src/types/v2/open-item.types.js.map +1 -0
  122. package/dist/cli/backend/src/types/v2/request.types.d.ts +22 -1
  123. package/dist/cli/backend/src/types/v2/request.types.d.ts.map +1 -1
  124. package/dist/cli/backend/src/types/v2/request.types.js +10 -5
  125. package/dist/cli/backend/src/types/v2/request.types.js.map +1 -1
  126. package/frontend/dist/assets/{index-9c91de50.js → index-a6cb8533.js} +265 -265
  127. package/frontend/dist/index.html +1 -1
  128. package/package.json +1 -1
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Find the commitments and questions an agent left open in a reply to the
3
+ * owner (specs/2026-10-01-reply-open-items.md §1). Pure, rules only.
4
+ *
5
+ * Tuned to miss rather than invent: a missed promise costs one follow-up the
6
+ * owner has to ask for himself; an invented question puts a pointless card in
7
+ * front of him. So a sentence only counts when it clearly says
8
+ *
9
+ * - **commitment**: a first-person future deliverable for the owner
10
+ * ("明天中午给我,我核过以后挑最有用的几条发你", "I'll send the draft tonight");
11
+ * - **question**: a direct question that asks the owner to decide or confirm
12
+ * ("这个读法,你同意吗?", "Should I keep the appendix?").
13
+ *
14
+ * Skipped on purpose: past tense ("已经发你了", "attached below"), conditional
15
+ * offers ("要的话我再…", "if you want, I'll…"), quoted text, headings,
16
+ * questions to a colleague (an @-mention or a teammate's name up front) and
17
+ * rhetorical questions that the agent answers itself.
18
+ *
19
+ * @module services/open-items/open-item-extractor
20
+ */
21
+ import { OPEN_ITEMS_CONSTANTS } from '../../constants.js';
22
+ /**
23
+ * Lines that are not prose: headings, quotes, code.
24
+ *
25
+ * @param line - Trimmed line
26
+ * @returns True to skip the line
27
+ */
28
+ function isNonProseLine(line) {
29
+ if (/^(#{1,6}\s|>|```|\|)/.test(line))
30
+ return true;
31
+ // A whole line in bold/italics is a heading ("*为什么是你?*").
32
+ if (/^[*_]{1,2}[^*_]+[*_]{1,2}[::]?$/.test(line))
33
+ return true;
34
+ return false;
35
+ }
36
+ const OPEN_QUOTES = '「『“‘《((';
37
+ const CLOSE_QUOTES = '」』”’》))';
38
+ /**
39
+ * Split one line into sentences at 。!?!?;; (and ". " before a capital or
40
+ * CJK), never inside quotes or brackets, keeping each terminator.
41
+ *
42
+ * @param line - One line
43
+ * @returns Sentences
44
+ */
45
+ function splitLine(line) {
46
+ const out = [];
47
+ let depth = 0;
48
+ let cur = '';
49
+ const chars = [...line];
50
+ for (let i = 0; i < chars.length; i++) {
51
+ const ch = chars[i];
52
+ cur += ch;
53
+ if (OPEN_QUOTES.includes(ch))
54
+ depth += 1;
55
+ else if (CLOSE_QUOTES.includes(ch))
56
+ depth = Math.max(0, depth - 1);
57
+ if (depth > 0)
58
+ continue;
59
+ const next = chars[i + 1];
60
+ const cut = '。!?!?;;'.includes(ch) || (ch === '.' && next === ' ' && /[A-Z\u4e00-\u9fff]/u.test(chars[i + 2] ?? ''));
61
+ // Keep closing punctuation right after the terminator ("?)", "。」").
62
+ if (cut && !(next && CLOSE_QUOTES.includes(next))) {
63
+ if (cur.trim())
64
+ out.push(cur.trim());
65
+ cur = '';
66
+ }
67
+ }
68
+ if (cur.trim())
69
+ out.push(cur.trim());
70
+ return out;
71
+ }
72
+ /**
73
+ * Split a reply into sentences, line by line.
74
+ *
75
+ * @param text - Reply text
76
+ * @returns Sentences
77
+ */
78
+ export function splitSentences(text) {
79
+ const out = [];
80
+ const noCode = text.replace(/```[\s\S]*?```/g, ' ');
81
+ for (const raw of noCode.split(/\n+/)) {
82
+ const line = raw.replace(/^\s*(?:[-*•·]|\d+[.)、])\s+/, '').trim();
83
+ if (!line || isNonProseLine(line))
84
+ continue;
85
+ const parts = splitLine(line);
86
+ parts.forEach((p, i) => out.push({ text: p, index: i, line: parts }));
87
+ }
88
+ return out;
89
+ }
90
+ /**
91
+ * Whether the sentence's question mark sits inside quotes (a quoted question,
92
+ * e.g. 早报里问你一句「你怎么看?」).
93
+ *
94
+ * @param s - Sentence
95
+ * @returns True when every ?/? is inside quotes
96
+ */
97
+ function questionIsQuoted(s) {
98
+ let depth = 0;
99
+ let unquoted = false;
100
+ for (const ch of s) {
101
+ if ('「『“‘《'.includes(ch))
102
+ depth += 1;
103
+ else if ('」』”’》'.includes(ch))
104
+ depth = Math.max(0, depth - 1);
105
+ else if ((ch === '?' || ch === '?') && depth === 0)
106
+ unquoted = true;
107
+ }
108
+ return !unquoted;
109
+ }
110
+ /**
111
+ * Clip to one line of at most `max` characters.
112
+ *
113
+ * @param s - Text
114
+ * @param max - Max characters
115
+ * @returns Clipped text
116
+ */
117
+ function clip(s, max) {
118
+ const flat = s.replace(/\s+/g, ' ').replace(/^[*_]+|[*_]+$/g, '').trim();
119
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
120
+ }
121
+ /**
122
+ * Whether a sentence is addressed to someone other than the owner: an
123
+ * @-mention of another user, or a colleague's name opening it ("Kai,你…").
124
+ *
125
+ * @param s - Sentence
126
+ * @param opts - Colleague names, owner id
127
+ * @returns True when it is for a colleague
128
+ */
129
+ function addressedToColleague(s, opts) {
130
+ for (const m of s.matchAll(/<@([A-Z0-9]+)>/g)) {
131
+ if (!opts.ownerSlackUserId || m[1] !== opts.ownerSlackUserId)
132
+ return true;
133
+ }
134
+ if (/(^|\s)@[A-Za-z一-鿿][\w一-鿿-]*/u.test(s.replace(/<@[A-Z0-9]+>/g, '')))
135
+ return true;
136
+ for (const name of opts.colleagueNames ?? []) {
137
+ const n = name.trim();
138
+ if (n.length < 2)
139
+ continue;
140
+ const esc = n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
141
+ if (new RegExp(`^${esc}\\s*[,,::]`, 'i').test(s))
142
+ return true;
143
+ }
144
+ return false;
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // Commitments
148
+ // ---------------------------------------------------------------------------
149
+ /** A deliverable for the owner (Chinese): 发你 / 给你 / 告诉你 / 发在这里 … */
150
+ const ZH_DELIVER = /(?:发|给|交|传|带|拿|报|汇报|同步|告诉|通知|回复|回|贴|放|整理|写|做|出|补|帮)(?:一下|一份|一版|一个|几条|上来)?(?:给)?(?:你|您)(?![们的])|(?:发|贴|放|交)(?:在|到)(?:这里|这儿|这个线程|群里|频道)|(?:发|贴|交)上来|给你看|拿给你|报给你/u;
151
+ /** Future markers (Chinese). */
152
+ const ZH_FUTURE = /明天|明早|明晚|今晚|今天|后天|下周|周[一二三四五六日天]|星期[一二三四五六日天]|礼拜[一二三四五六日天]|稍后|待会|一会儿|回头|晚点|之后|以后|再|会|先|小时|分钟|中午|下午|晚上|早上|上午|[好完](?:以后|之后|后)|出来[后以]|核过|过一遍|一起/u;
153
+ /** Past / already-done markers (Chinese). */
154
+ const ZH_PAST = /已经|已(?:发|放|附|写|改|存|同步|上传)|刚才|刚刚|附在|附上了|见下|如下|下面附|现在(?:先)?给(?:你|您)|这是|下面是|(?:你|您)了[,。!,.!]?$|(?:你|您)了[,,]/u;
155
+ /** Standing habits, not one deliverable: "以后每章都给你发 PDF", "from now on". */
156
+ const HABITUAL = /每(?:次|章|天|周|个|篇|期|回|晚|早)|以后都|今后|往后|所有|一律|都先|都按|照常|from now on|every (?:time|day|week|chapter)|each time/iu;
157
+ /** Conditional offers (Chinese): "要的话我再…", "如果你想…". */
158
+ const ZH_CONDITIONAL = /如果|要是|假如|的话|需要的话|想要的话/u;
159
+ /** First-person future deliverable (English). */
160
+ const EN_COMMIT = /\b(?:i'?ll|i will|i'm going to|i am going to|we'?ll|we will|i'?m gonna|i can have)\b[^.?!]{0,80}?\b(?:send|share|post|deliver|get back|circle back|follow up|report back|update you|let you know|bring|hand over|draft|write up|put together|wrap up|finish|have (?:it|this|that|them|the \w+)|get (?:it|this|that|them) to you)\b/i;
161
+ /** Conditional / past (English). */
162
+ const EN_SKIP = /\b(?:if|unless|otherwise|whenever|in case|already|attached|below|above|have sent|sent you|i'?d)\b/i;
163
+ /** Someone other than the agent's side does the delivering ("别人第二天白天回你"). */
164
+ const ZH_THIRD_PARTY = /^(?:别人|他们|她们|他|她|它|对方|大家|有人|读者|用户|客户|网友|粉丝|对面)/u;
165
+ /**
166
+ * Whether the clause holding the deliverable has a third party as subject.
167
+ *
168
+ * @param t - Sentence (quotes removed)
169
+ * @returns True when someone outside the team delivers
170
+ */
171
+ function thirdPartyDelivers(t) {
172
+ const clause = t.split(/[,,;;::]/).find((c) => ZH_DELIVER.test(c));
173
+ return !!clause && ZH_THIRD_PARTY.test(clause.trim());
174
+ }
175
+ /**
176
+ * Whether a sentence is a commitment to the owner.
177
+ *
178
+ * @param s - Sentence
179
+ * @returns True for a first-person future deliverable
180
+ */
181
+ export function isCommitment(s) {
182
+ // Quoted words are someone else's ("…原话:「10 分钟就给你做出…」").
183
+ // So are bracketed asides ("(…Gemini 带你玩夏威夷…)").
184
+ const t = s.replace(/「[^」]*」|『[^』]*』|“[^”]*”|"[^"]*"|([^)]*)|\([^)]*\)/g, ' ').trim();
185
+ if (/[??]\s*$/.test(t))
186
+ return false;
187
+ if (thirdPartyDelivers(t))
188
+ return false;
189
+ if (HABITUAL.test(t))
190
+ return false;
191
+ if (/[一-鿿]/.test(t) && ZH_DELIVER.test(t)) {
192
+ if (ZH_PAST.test(t) || ZH_CONDITIONAL.test(t))
193
+ return false;
194
+ return ZH_FUTURE.test(t);
195
+ }
196
+ if (EN_COMMIT.test(t))
197
+ return !EN_SKIP.test(t);
198
+ return false;
199
+ }
200
+ // ---------------------------------------------------------------------------
201
+ // Due times
202
+ // ---------------------------------------------------------------------------
203
+ const WEEKDAY_ZH = { 日: 0, 天: 0, 一: 1, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6 };
204
+ const WEEKDAY_EN = { sunday: 0, monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6 };
205
+ /**
206
+ * A local date `days` after `now` at `hour`:00.
207
+ *
208
+ * @param now - Clock
209
+ * @param days - Days ahead
210
+ * @param hour - Local hour
211
+ * @returns Date
212
+ */
213
+ function dayAt(now, days, hour) {
214
+ const d = new Date(now.getTime());
215
+ d.setDate(d.getDate() + days);
216
+ d.setHours(hour, 0, 0, 0);
217
+ return d;
218
+ }
219
+ /**
220
+ * The hour a part-of-day word names, or null.
221
+ *
222
+ * @param t - Lower-cased text
223
+ * @returns Local hour
224
+ */
225
+ function partOfDayHour(t) {
226
+ if (/中午|\bnoon\b|\blunch/.test(t))
227
+ return 12;
228
+ if (/傍晚|下班|end of (?:the )?day|\beod\b/.test(t))
229
+ return 18;
230
+ if (/下午|afternoon/.test(t))
231
+ return 15;
232
+ if (/晚上|晚|evening|night|tonight/.test(t))
233
+ return 21;
234
+ if (/早上|上午|早|morning/.test(t))
235
+ return 10;
236
+ return null;
237
+ }
238
+ /**
239
+ * When a commitment is due.
240
+ *
241
+ * Rules (local time): an explicit day/part of day wins ("明天中午" → tomorrow
242
+ * 12:00, "tonight" → today 21:00, "by Friday" → Friday 12:00); "tomorrow"
243
+ * alone → tomorrow {@link OPEN_ITEMS_CONSTANTS.DEFAULT_DUE_HOUR_LOCAL}:00;
244
+ * "in 2 hours" → +2 h; nothing → +{@link OPEN_ITEMS_CONSTANTS.DEFAULT_DUE_MS}.
245
+ *
246
+ * @param s - Sentence
247
+ * @param now - When it was said
248
+ * @returns Due time and whether the text named it
249
+ */
250
+ export function parseDue(s, now) {
251
+ const t = s.toLowerCase();
252
+ const defaultHour = OPEN_ITEMS_CONSTANTS.DEFAULT_DUE_HOUR_LOCAL;
253
+ const text = (due) => ({ due, source: 'text' });
254
+ const rel = /(\d+(?:\.\d+)?)\s*(?:个)?\s*(小时|hours?|hrs?|分钟|minutes?|mins?)/.exec(t);
255
+ if (rel) {
256
+ const n = Number(rel[1]);
257
+ const ms = /小时|hour|hr/.test(rel[2]) ? n * 3600_000 : n * 60_000;
258
+ if (ms > 0)
259
+ return text(new Date(now.getTime() + ms));
260
+ }
261
+ if (/半小时|half an hour/.test(t))
262
+ return text(new Date(now.getTime() + 1800_000));
263
+ // "晚点" / "早点" mean later / sooner, not evening / morning.
264
+ const part = partOfDayHour(t.replace(/晚点|早点|晚些|早些/g, ''));
265
+ if (/后天|day after tomorrow/.test(t))
266
+ return text(dayAt(now, 2, part ?? defaultHour));
267
+ if (/明早/.test(t))
268
+ return text(dayAt(now, 1, 10));
269
+ if (/明晚/.test(t))
270
+ return text(dayAt(now, 1, 21));
271
+ if (/明天|tomorrow/.test(t))
272
+ return text(dayAt(now, 1, part ?? defaultHour));
273
+ if (/今晚|tonight/.test(t)) {
274
+ const d = dayAt(now, 0, 21);
275
+ return text(d.getTime() > now.getTime() ? d : new Date(now.getTime() + 2 * 3600_000));
276
+ }
277
+ const zhDay = /(下)?(?:周|星期|礼拜)([一二三四五六日天])/.exec(s);
278
+ const enDay = /\b(next\s+)?(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\b/.exec(t);
279
+ if (zhDay || enDay) {
280
+ const target = zhDay ? WEEKDAY_ZH[zhDay[2]] : WEEKDAY_EN[enDay[2]];
281
+ const nextWeek = !!(zhDay ? zhDay[1] : enDay[1]);
282
+ let days = (target - now.getDay() + 7) % 7 || 7;
283
+ if (nextWeek) {
284
+ // "下周三" / "next Wednesday": the one in the coming (Monday-start) week.
285
+ const toNextMonday = (1 - now.getDay() + 7) % 7 || 7;
286
+ days = toNextMonday + ((target + 6) % 7);
287
+ }
288
+ return text(dayAt(now, days, part ?? defaultHour));
289
+ }
290
+ if (/下周|next week/.test(t)) {
291
+ const days = ((1 - now.getDay() + 7) % 7) || 7;
292
+ return text(dayAt(now, days, defaultHour));
293
+ }
294
+ if (/今天|today|later today|end of (?:the )?day|eod|下班前/.test(t) || (part !== null && !/明|tomorrow/.test(t))) {
295
+ const d = dayAt(now, 0, part ?? 18);
296
+ if (d.getTime() > now.getTime())
297
+ return text(d);
298
+ return text(new Date(now.getTime() + 3 * 3600_000));
299
+ }
300
+ if (/稍后|待会|一会儿|晚点|马上|soon|shortly|in a bit|later/.test(t)) {
301
+ return text(new Date(now.getTime() + 2 * 3600_000));
302
+ }
303
+ return { due: new Date(now.getTime() + OPEN_ITEMS_CONSTANTS.DEFAULT_DUE_MS), source: 'default' };
304
+ }
305
+ // ---------------------------------------------------------------------------
306
+ // Questions
307
+ // ---------------------------------------------------------------------------
308
+ /**
309
+ * Asks the owner to decide or confirm (Chinese): a yes/no or either/or shape.
310
+ * Open information questions ("你们每周花多少小时?") are left alone — a card
311
+ * cannot answer them, and the agent sees the owner's reply anyway.
312
+ */
313
+ const ZH_ASK = /吗|要不要|是不是|是否|可不可以|能不能|行不行|好不好|同意|确认|批准|拍板|还是|对吧|对不对|OK|ok|定一下/u;
314
+ /** Asks the owner to decide or confirm (English): yes/no or either/or. */
315
+ const EN_ASK = /^(?:so,?\s+)?(?:should|shall|do|does|can|could|would|will|is|are|may|want|ok|okay|any objection|happy|good)\b|\bwant me to\b|\bok(?:ay)?\s*\?$|\bright\s*\?$|\bor\b[^?]*\?$/i;
316
+ /** Rhetorical openers: the agent is not asking. */
317
+ const RHETORICAL_OPEN = /^(?:难道|凭什么|谁说|岂不是|何必|你可能会问|你也许会问|有人会问|为什么(?!你)|what if\b|who says\b|isn'?t it\b|why would\b|you might ask\b|you may ask\b|why\b(?! did you| do you| would you))/iu;
318
+ /** A sentence that answers the question before it. */
319
+ const SELF_ANSWER = /^(?:因为|答案|原因是|原因在于|其实|简单说|because\b|the answer\b|that'?s because\b|simple[::])/iu;
320
+ /**
321
+ * Whether a sentence (with its neighbours) is a question for the owner.
322
+ *
323
+ * @param s - The sentence
324
+ * @param next - The sentence after it on the same line, if any
325
+ * @param opts - Extraction options
326
+ * @returns True for a decision/confirmation question to the owner
327
+ */
328
+ export function isOwnerQuestion(s, next, opts) {
329
+ const t = s.trim();
330
+ if (!/[??]\s*[))]?$/.test(t))
331
+ return false;
332
+ if (questionIsQuoted(t))
333
+ return false;
334
+ if (RHETORICAL_OPEN.test(t))
335
+ return false;
336
+ if (next && SELF_ANSWER.test(next.trim()))
337
+ return false;
338
+ if (addressedToColleague(t, opts))
339
+ return false;
340
+ const core = t.replace(/[??\s]+$/u, '');
341
+ if (core.replace(/[\s*_]/g, '').length < 4)
342
+ return false;
343
+ return /[一-鿿]/.test(t) ? ZH_ASK.test(t) : EN_ASK.test(t);
344
+ }
345
+ /**
346
+ * A fallback sentence: what the agent does without an answer ("不同意的话我就删掉").
347
+ *
348
+ * @param s - Sentence after the question
349
+ * @returns True when it states a fallback
350
+ */
351
+ function isFallback(s) {
352
+ if (!s)
353
+ return false;
354
+ return /的话|否则|不然|if not\b|otherwise\b|if you don'?t\b|if you disagree\b|unless you\b|if i don'?t hear\b|if there'?s no objection\b|if no objection\b/iu.test(s);
355
+ }
356
+ // ---------------------------------------------------------------------------
357
+ // Entry point
358
+ // ---------------------------------------------------------------------------
359
+ /**
360
+ * Find open items in an agent's reply to the owner.
361
+ *
362
+ * @param text - The reply
363
+ * @param opts - Clock, colleague names, owner id
364
+ * @returns Commitments and questions (each at most
365
+ * {@link OPEN_ITEMS_CONSTANTS.MAX_ITEMS_PER_REPLY})
366
+ *
367
+ * @example
368
+ * extractOpenItems('明天中午给我,我核过以后挑最有用的几条发你。', { now })
369
+ * // → { commitments: [{ text: '明天中午给我,我核过以后挑最有用的几条发你。', due: <tomorrow 12:00>, … }], questions: [] }
370
+ */
371
+ export function extractOpenItems(text, opts) {
372
+ const out = { commitments: [], questions: [] };
373
+ if (!text || !text.trim())
374
+ return out;
375
+ const max = OPEN_ITEMS_CONSTANTS.MAX_ITEMS_PER_REPLY;
376
+ for (const sentence of splitSentences(text)) {
377
+ const s = sentence.text;
378
+ const next = sentence.line[sentence.index + 1];
379
+ if (out.questions.length < max && isOwnerQuestion(s, next, opts)) {
380
+ out.questions.push({
381
+ type: 'question',
382
+ text: clip(s, OPEN_ITEMS_CONSTANTS.QUESTION_MAX_CHARS),
383
+ ...(isFallback(next) ? { fallback: clip(next, OPEN_ITEMS_CONSTANTS.QUESTION_MAX_CHARS) } : {}),
384
+ });
385
+ continue;
386
+ }
387
+ if (out.commitments.length < max && !addressedToColleague(s, opts) && isCommitment(s)) {
388
+ const { due, source } = parseDue(s, opts.now);
389
+ out.commitments.push({ type: 'commitment', text: clip(s, OPEN_ITEMS_CONSTANTS.TEXT_MAX_CHARS), due, dueSource: source });
390
+ }
391
+ }
392
+ return out;
393
+ }
394
+ //# sourceMappingURL=open-item-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-item-extractor.js","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-item-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * One-off backfill: scan tickets of the last 7 days for commitments and
3
+ * questions their agents left open before open-item tracking existed
4
+ * (specs/2026-10-01-reply-open-items.md §6). Dry-run by default: it reports
5
+ * what it WOULD create and changes nothing.
6
+ *
7
+ * An item already settled by the conversation is skipped:
8
+ * - a commitment the agent (or the child's agent) delivered later in the
9
+ * thread, by the live delivery rule;
10
+ * - a question the owner replied to later in the thread.
11
+ *
12
+ * @module services/open-items/open-items-backfill
13
+ */
14
+ import type { Request } from '../../types/v2/request.types.js';
15
+ import type { WorkItem } from '../../types/v2/work-item.types.js';
16
+ import { type OpenItemsChatMessage, type OpenItemsService } from './open-items.service.js';
17
+ /** Readers the backfill needs. */
18
+ export interface BackfillDeps {
19
+ service: Pick<OpenItemsService, 'plan' | 'adopt' | 'deliveredBy' | 'findRequestFor'>;
20
+ listRequests: () => Promise<Request[]>;
21
+ listWorkItems: () => Promise<WorkItem[]>;
22
+ /** Every message of a conversation thread (root + replies), oldest first */
23
+ listThread: (channelId: string, rootId: string) => Promise<OpenItemsChatMessage[]>;
24
+ now?: () => Date;
25
+ }
26
+ /** One row of the report. */
27
+ export interface BackfillRow {
28
+ requestId: string;
29
+ ticket: string;
30
+ requestStatus: string;
31
+ agent: string;
32
+ type: 'commitment' | 'question';
33
+ text: string;
34
+ /** When the agent said it */
35
+ at: string;
36
+ /** What the backfill does with it */
37
+ action: string;
38
+ /** Commitment: due time */
39
+ due?: string;
40
+ /** Commitment: child work it waits on (id: status) */
41
+ children?: string[];
42
+ /** Why it is skipped (already settled) */
43
+ skipped?: string;
44
+ }
45
+ /** The report. */
46
+ export interface BackfillReport {
47
+ dryRun: boolean;
48
+ scanned: number;
49
+ rows: BackfillRow[];
50
+ /** Tickets that would move from done to awaiting_followup */
51
+ reopened: string[];
52
+ }
53
+ /**
54
+ * Run the backfill.
55
+ *
56
+ * @param deps - Readers and the service
57
+ * @param opts - `apply: true` to make the changes (default: dry-run)
58
+ * @returns What it found (and, when applied, did)
59
+ */
60
+ export declare function backfillOpenItems(deps: BackfillDeps, opts?: {
61
+ apply?: boolean;
62
+ }): Promise<BackfillReport>;
63
+ /**
64
+ * Plain-text rendering of a report (the script's output).
65
+ *
66
+ * @param report - Report
67
+ * @returns Text
68
+ */
69
+ export declare function formatBackfillReport(report: BackfillReport): string;
70
+ //# sourceMappingURL=open-items-backfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-items-backfill.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-items-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAElE,OAAO,EAA2C,KAAK,oBAAoB,EAAE,KAAK,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAE1J,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,gBAAgB,CAAC,CAAC;IACrF,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACvC,aAAa,EAAE,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzC,4EAA4E;IAC5E,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACnF,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,6BAA6B;AAC7B,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,kBAAkB;AAClB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAiFnH;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAqBnE"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * One-off backfill: scan tickets of the last 7 days for commitments and
3
+ * questions their agents left open before open-item tracking existed
4
+ * (specs/2026-10-01-reply-open-items.md §6). Dry-run by default: it reports
5
+ * what it WOULD create and changes nothing.
6
+ *
7
+ * An item already settled by the conversation is skipped:
8
+ * - a commitment the agent (or the child's agent) delivered later in the
9
+ * thread, by the live delivery rule;
10
+ * - a question the owner replied to later in the thread.
11
+ *
12
+ * @module services/open-items/open-items-backfill
13
+ */
14
+ import { OPEN_ITEMS_CONSTANTS } from '../../constants.js';
15
+ import { formatTicketNumber } from '../../types/v2/ticket.types.js';
16
+ import { childrenState, childWorkFor, isSameItem } from './open-items.service.js';
17
+ /**
18
+ * Run the backfill.
19
+ *
20
+ * @param deps - Readers and the service
21
+ * @param opts - `apply: true` to make the changes (default: dry-run)
22
+ * @returns What it found (and, when applied, did)
23
+ */
24
+ export async function backfillOpenItems(deps, opts = {}) {
25
+ const apply = opts.apply === true;
26
+ const now = (deps.now ?? (() => new Date()))();
27
+ const since = now.getTime() - OPEN_ITEMS_CONSTANTS.LOOKBACK_MS;
28
+ const allRequests = await deps.listRequests();
29
+ const requests = allRequests.filter((r) => typeof r.ticketNumber === 'number' && !!r.chatRef && r.status !== 'cancelled' && Date.parse(r.updatedAt) >= since);
30
+ const pool = await deps.listWorkItems();
31
+ const report = { dryRun: !apply, scanned: requests.length, rows: [], reopened: [] };
32
+ for (const request of requests) {
33
+ const ref = request.chatRef;
34
+ const thread = (await deps.listThread(ref.channelId, ref.threadRootId)).sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));
35
+ const ticket = formatTicketNumber(request.ticketNumber);
36
+ const keep = [];
37
+ for (const message of thread) {
38
+ if (message.senderType !== 'agent' || Date.parse(new Date(message.createdAt ?? 0).toISOString()) < since)
39
+ continue;
40
+ if ((request.openItems ?? []).some((i) => i.sourceMessageId === message.id))
41
+ continue;
42
+ // One ticket per message, as the live path decides it.
43
+ if (deps.service.findRequestFor(message, allRequests)?.id !== request.id)
44
+ continue;
45
+ const at = new Date(message.createdAt ?? now.getTime());
46
+ const planned = await deps.service.plan(request, message, at);
47
+ const later = thread.filter((m) => (m.createdAt ?? 0) > (message.createdAt ?? 0));
48
+ for (const p of planned) {
49
+ if (keep.some((k) => isSameItem(k.item, p.item)))
50
+ continue;
51
+ const row = {
52
+ requestId: request.id,
53
+ ticket,
54
+ requestStatus: request.status,
55
+ agent: p.item.agent,
56
+ type: p.item.type,
57
+ text: p.item.text,
58
+ at: p.item.createdAt,
59
+ action: '',
60
+ };
61
+ if (p.item.type === 'commitment') {
62
+ const children = childWorkFor(request, p.item, pool);
63
+ const byId = new Map(pool.map((w) => [w.id, w]));
64
+ row.due = p.item.due;
65
+ row.children = children.map((id) => `${id.slice(0, 8)}: ${byId.get(id)?.status ?? '?'}`);
66
+ const item = { ...p.item, ...(children.length ? { childWorkItemIds: children } : {}) };
67
+ const delivery = later.find((m) => m.senderType === 'agent' && deps.service.deliveredBy(item, m, pool));
68
+ if (delivery) {
69
+ row.skipped = `delivered by ${delivery.senderId} at ${new Date(delivery.createdAt ?? 0).toISOString()}`;
70
+ row.action = 'none';
71
+ report.rows.push(row);
72
+ continue;
73
+ }
74
+ const state = childrenState(children, pool);
75
+ const overdue = p.item.due ? Date.parse(p.item.due) <= now.getTime() : false;
76
+ const parts = ['create follow-up WorkItem for ' + p.item.agent];
77
+ if (state.ready)
78
+ parts.push(`wake ${p.item.agent} now — the work is ready (finished ${state.finishedAt ?? '?'})`);
79
+ else if (state.pending.length)
80
+ parts.push(`wait for ${state.pending.map((w) => `${w.id.slice(0, 8)} (${w.status})`).join(', ')}`);
81
+ if (overdue)
82
+ parts.push('already past due → nudge on the next sweep');
83
+ row.action = parts.join('; ');
84
+ }
85
+ else {
86
+ const ownerReply = later.find((m) => m.senderType === 'user');
87
+ if (ownerReply) {
88
+ row.skipped = `owner replied at ${new Date(ownerReply.createdAt ?? 0).toISOString()}`;
89
+ row.action = 'none';
90
+ report.rows.push(row);
91
+ continue;
92
+ }
93
+ if (p.linkedDecisionId) {
94
+ row.action = `link to existing ask-owner card ${p.linkedDecisionId} (no new card)`;
95
+ }
96
+ else if (p.card) {
97
+ const opts = p.card.options.map((o) => `${o.label}${o.detail ? ` (${o.detail})` : ''}`).join(' / ');
98
+ const def = p.card.options.find((o) => o.key === p.card.defaultKey)?.label ?? p.card.defaultKey;
99
+ row.action = `post decision card as ${p.item.agent} in the ticket thread: ${opts}; default ${def}; deadline tomorrow 12:00${p.card.sensitive ? `; sensitive ${p.card.sensitive}` : ''}`;
100
+ }
101
+ }
102
+ keep.push(p);
103
+ report.rows.push(row);
104
+ }
105
+ }
106
+ if (keep.length === 0)
107
+ continue;
108
+ if (request.status === 'done')
109
+ report.reopened.push(`${ticket} (${request.id.slice(0, 8)})`);
110
+ if (apply)
111
+ await deps.service.adopt(request.id, keep);
112
+ }
113
+ return report;
114
+ }
115
+ /**
116
+ * Plain-text rendering of a report (the script's output).
117
+ *
118
+ * @param report - Report
119
+ * @returns Text
120
+ */
121
+ export function formatBackfillReport(report) {
122
+ const lines = [];
123
+ lines.push(`${report.dryRun ? 'DRY RUN — nothing changed' : 'APPLIED'}. Tickets scanned (last 7 days): ${report.scanned}.`);
124
+ const live = report.rows.filter((r) => !r.skipped);
125
+ const skipped = report.rows.filter((r) => r.skipped);
126
+ lines.push(`Open items to track: ${live.length} (${live.filter((r) => r.type === 'commitment').length} commitments, ${live.filter((r) => r.type === 'question').length} questions). Already settled, skipped: ${skipped.length}.`);
127
+ if (report.reopened.length)
128
+ lines.push(`Closed tickets that would move to awaiting_followup: ${report.reopened.join(', ')}`);
129
+ for (const r of live) {
130
+ lines.push('');
131
+ lines.push(`${r.ticket} [${r.requestStatus}] ${r.type.toUpperCase()} by ${r.agent} at ${r.at}`);
132
+ lines.push(` "${r.text}"`);
133
+ if (r.due)
134
+ lines.push(` due: ${r.due}`);
135
+ if (r.children?.length)
136
+ lines.push(` child work: ${r.children.join(', ')}`);
137
+ lines.push(` → ${r.action}`);
138
+ }
139
+ if (skipped.length) {
140
+ lines.push('');
141
+ lines.push('Skipped (already settled in the thread):');
142
+ for (const r of skipped)
143
+ lines.push(` ${r.ticket} ${r.type} by ${r.agent}: "${r.text.slice(0, 80)}" — ${r.skipped}`);
144
+ }
145
+ return lines.join('\n');
146
+ }
147
+ //# sourceMappingURL=open-items-backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-items-backfill.js","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-items-backfill.ts"],"names":[],"mappings":"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"}