typeclaw 0.37.3 → 0.37.5

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 (58) hide show
  1. package/README.md +69 -46
  2. package/package.json +1 -1
  3. package/src/agent/compaction.ts +24 -15
  4. package/src/agent/doctor.ts +6 -1
  5. package/src/agent/session-origin.ts +101 -173
  6. package/src/agent/subagents.ts +146 -14
  7. package/src/agent/system-prompt.ts +46 -48
  8. package/src/agent/todo/scope.ts +4 -2
  9. package/src/agent/tools/channel-reply.ts +7 -9
  10. package/src/bundled-plugins/memory/index.ts +33 -33
  11. package/src/bundled-plugins/memory/load-memory.ts +92 -35
  12. package/src/bundled-plugins/memory/slug.ts +19 -0
  13. package/src/bundled-plugins/memory/turn-dedup.ts +32 -29
  14. package/src/bundled-plugins/security/policies/private-surface-read.ts +4 -1
  15. package/src/bundled-plugins/tool-result-cap/README.md +7 -7
  16. package/src/bundled-plugins/tool-result-cap/index.ts +1 -1
  17. package/src/channels/adapters/discord-bot.ts +11 -4
  18. package/src/channels/adapters/github/inbound.ts +68 -43
  19. package/src/channels/adapters/github/index.ts +57 -9
  20. package/src/channels/adapters/github/recover-failed-deliveries.ts +270 -0
  21. package/src/channels/adapters/kakaotalk.ts +5 -1
  22. package/src/channels/adapters/mention-hints.ts +75 -0
  23. package/src/channels/adapters/slack-bot.ts +8 -2
  24. package/src/channels/continuation-willingness.ts +216 -68
  25. package/src/channels/router.ts +149 -15
  26. package/src/cli/dreams.ts +2 -2
  27. package/src/cli/init.ts +41 -7
  28. package/src/cli/inspect.ts +2 -2
  29. package/src/cli/logs.ts +2 -2
  30. package/src/cli/qr.ts +4 -3
  31. package/src/cli/require-agent-dir.ts +31 -0
  32. package/src/cli/shell.ts +2 -2
  33. package/src/cli/stop.ts +2 -2
  34. package/src/cli/tui.ts +20 -6
  35. package/src/cli/ui.ts +8 -4
  36. package/src/container/shared.ts +18 -0
  37. package/src/container/start.ts +1 -1
  38. package/src/doctor/checks.ts +145 -2
  39. package/src/hostd/client.ts +48 -52
  40. package/src/hostd/daemon.ts +82 -39
  41. package/src/hostd/paths.ts +22 -2
  42. package/src/hostd/spawn.ts +7 -0
  43. package/src/hostd/tailscale.ts +12 -1
  44. package/src/init/index.ts +35 -8
  45. package/src/init/kakaotalk-auth.ts +2 -2
  46. package/src/init/packagejson.ts +2 -2
  47. package/src/init/run-bun-install.ts +71 -37
  48. package/src/inspect/transcript-view.ts +15 -2
  49. package/src/plugin/loader.ts +7 -4
  50. package/src/portbroker/hostd-client.ts +32 -6
  51. package/src/sandbox/session-tmp.ts +6 -1
  52. package/src/secrets/export-claude-credentials-file.ts +2 -2
  53. package/src/shared/index.ts +4 -0
  54. package/src/shared/platform.ts +11 -0
  55. package/src/shared/wsl.ts +139 -0
  56. package/src/tui/index.ts +26 -8
  57. package/src/tui/terminal-guard.ts +139 -0
  58. package/typeclaw.schema.json +2 -2
@@ -13,6 +13,14 @@
13
13
  // descriptive ("I checked and it's fine") or other-directed ("you can continue")
14
14
  // usage. This is a HINT, not a control-flow authority: the abort still fires
15
15
  // regardless; only the optional nudge is gated on it.
16
+ //
17
+ // Detection is two-pass: a phrase-substring pass (ALL_PHRASES) for analytic
18
+ // languages where future intent is a separate word ("I'll", "voy a", "我会"),
19
+ // plus a morpheme pass (MORPHEME_PATTERNS + the Japanese check) for languages
20
+ // where it is a verb inflection/affix. The morpheme pass matches the marker
21
+ // itself, so it generalizes across EVERY action verb (update/configure/fix/…)
22
+ // instead of enumerating each — what the per-verb KO/TR/HI/JA lists used to do
23
+ // by hand.
16
24
 
17
25
  // Strip markdown emphasis/code fences before matching so an inline `gh` span
18
26
  // inside "바로 `gh`로 확인할게요" does not split the phrase.
@@ -62,70 +70,80 @@ const EN_PHRASES: readonly string[] = [
62
70
  'lemme check',
63
71
  'lemme look',
64
72
  'lemme take a look',
73
+ // Action/config verb family. The retrieval verbs above ("check/look/look up")
74
+ // miss the much larger class of "I'll DO X" promises — update/configure/set up/
75
+ // schedule/fix/apply/create — which is exactly the class that silently truncates
76
+ // when the model forgets `continue: true` (the cron-update production miss).
77
+ "i'll update",
78
+ "i'll set up",
79
+ "i'll set it up",
80
+ "i'll configure",
81
+ "i'll schedule",
82
+ "i'll fix",
83
+ "i'll apply",
84
+ "i'll add",
85
+ "i'll create",
86
+ "i'll handle",
87
+ 'let me update',
88
+ 'let me fix',
89
+ 'let me set up',
90
+ 'let me configure',
91
+ 'let me add',
92
+ 'let me create',
93
+ 'let me handle',
65
94
  ]
66
95
 
67
- // Korean: -ㄹ게요 / -겠습니다 future-volitional endings on check/look/continue/
68
- // proceed verbs. These endings are first-person volitional in Korean they
69
- // cannot address the listener, so they are safe self-direction anchors that
70
- // descriptive or other-directed sentences do not produce. Bare "계속" is
71
- // excluded ("계속 진행하세요" = "you go ahead", terminal).
96
+ // Korean: the -겠습니다/-겠어요 and -ㄹ게요 verb endings are first-person
97
+ // volitional they cannot address the listener, so they are safe self-direction
98
+ // anchors. The -겠습니다/-겠어요 form is matched by MORPHEME_PATTERNS below (it
99
+ // generalizes across all action verbs), so only the -게요/-게여 forms and stall
100
+ // idioms are enumerated here. Bare adverb+noun fragments ("바로 확인", "계속 확인",
101
+ // "곧 알려") are deliberately NOT listed: without the volitional ending they match
102
+ // other-directed requests ("바로 확인 부탁드려요" = "please check") and descriptive
103
+ // progressives ("계속 확인 중입니다" = "I'm still checking") — the exact false
104
+ // positives the design forbids. Their volitional forms are caught by the morpheme
105
+ // regex regardless.
72
106
  const KO_PHRASES: readonly string[] = [
73
107
  '확인해볼게요',
74
108
  '확인해 볼게요',
75
109
  '확인할게요',
76
- '확인하겠습니다',
77
- '확인해보겠습니다',
78
- '확인해 보겠습니다',
79
- '다시 확인하겠습니다',
80
- '다시 확인해보겠습니다',
81
- '이어서 확인',
82
- '계속 확인',
110
+ '확인할게여',
83
111
  '계속 진행할게요',
84
- '계속 진행하겠습니다',
85
- '계속하겠습니다',
86
112
  '계속할게요',
87
- '바로 확인',
88
113
  '바로 볼게요',
89
- '바로 진행',
90
114
  '살펴볼게요',
91
- '살펴보겠습니다',
92
- '진행하겠습니다',
93
- '잠시만요',
94
- '잠깐만요',
95
- '곧 알려',
96
- // Bare first-person-volitional verb endings: the -ㄹ게요/-겠습니다 ending is
97
- // self-directed regardless of the preceding adverb, so the "바로 …" prefix in
98
- // the entries above is not load-bearing. "볼게요" alone (and "먼저/한번/지금 볼게요"
99
- // by substring) is the exact production miss — the ack "…먼저 볼게요" did not
100
- // match because only the "바로 볼게요" compound was listed. Common work verbs
101
- // (검토/조회/찾아/알아/처리) in the same volitional form join here for parity with
102
- // "확인/살펴" above; "볼게여" is the casual -여 variant seen in chat.
103
115
  '볼게요',
104
116
  '볼게여',
105
- '확인할게여',
106
117
  '검토할게요',
107
118
  '검토해볼게요',
108
- '검토하겠습니다',
109
119
  '조회해볼게요',
110
- '조회하겠습니다',
111
120
  '찾아볼게요',
112
- '찾아보겠습니다',
113
121
  '알아볼게요',
114
- '알아보겠습니다',
115
122
  '처리할게요',
116
- '처리하겠습니다',
123
+ '알려드릴게요',
124
+ // Action/config verb -게요 forms (the -겠습니다 siblings are covered by the
125
+ // morpheme regex; these are the casual-polite variants chat models also emit).
126
+ '업데이트할게요',
127
+ '수정할게요',
128
+ '설정할게요',
129
+ '반영할게요',
130
+ '적용할게요',
131
+ '추가할게요',
132
+ '생성할게요',
133
+ '잠시만요',
134
+ '잠깐만요',
117
135
  ]
118
136
 
119
137
  // The remaining languages mirror the precision-first selection above: every
120
138
  // entry pairs a FIRST-PERSON future/volitional anchor with a work verb
121
- // (check/look/continue/proceed/verify) or is an immediate-work idiom ("on it
122
- // now"). The same false-negative bias holds — bare verbs, bare acknowledgments
123
- // ("ok", "sí", "好"), second-person imperatives ("you continue"), and
124
- // descriptive past forms ("I checked") are deliberately excluded because a
125
- // substring match on those would mis-fire. Latin/Cyrillic/Arabic/Indic entries
126
- // are inflected first-person-future forms (or multi-word) so they cannot
127
- // collide with a bare common word; CJK entries are full 4+ character
128
- // intent phrases, never a lone noun.
139
+ // (check/look/continue/proceed/verify or update/configure/fix/create) or is an
140
+ // immediate-work idiom ("on it now"). The same false-negative bias holds — bare
141
+ // verbs, bare acknowledgments ("ok", "sí", "好"), second-person imperatives ("you
142
+ // continue"), and descriptive past forms ("I checked") are deliberately excluded
143
+ // because a substring match on those would mis-fire. Latin/Cyrillic/Arabic/Indic
144
+ // entries are inflected first-person-future forms (or multi-word) so they cannot
145
+ // collide with a bare common word; CJK entries are full 4+ character intent
146
+ // phrases, never a lone noun.
129
147
 
130
148
  // Spanish: "voy a" / "déjame" + work verb; "enseguida" (right away) idioms.
131
149
  const ES_PHRASES: readonly string[] = [
@@ -152,6 +170,17 @@ const ES_PHRASES: readonly string[] = [
152
170
  'un momento',
153
171
  'dame un momento',
154
172
  'dame un segundo',
173
+ // Action/config verb family.
174
+ 'voy a actualizar',
175
+ 'voy a configurar',
176
+ 'voy a corregir',
177
+ 'voy a arreglar',
178
+ 'voy a crear',
179
+ 'voy a añadir',
180
+ 'voy a programar',
181
+ 'voy a aplicar',
182
+ 'déjame actualizar',
183
+ 'déjame corregir',
155
184
  ]
156
185
 
157
186
  // French: "je vais" + work verb; "laisse-moi" idioms.
@@ -174,6 +203,16 @@ const FR_PHRASES: readonly string[] = [
174
203
  'un instant',
175
204
  'donne-moi un instant',
176
205
  'donne-moi une seconde',
206
+ // Action/config verb family.
207
+ 'je vais mettre à jour',
208
+ 'je vais configurer',
209
+ 'je vais corriger',
210
+ 'je vais créer',
211
+ 'je vais ajouter',
212
+ 'je vais programmer',
213
+ 'je vais appliquer',
214
+ 'laisse-moi corriger',
215
+ 'laisse-moi mettre à jour',
177
216
  ]
178
217
 
179
218
  // Italian: "vado a" / "fammi" + work verb; "controllo subito" idioms.
@@ -194,6 +233,15 @@ const IT_PHRASES: readonly string[] = [
194
233
  'un momento',
195
234
  'dammi un momento',
196
235
  'dammi un secondo',
236
+ // Action/config verb family.
237
+ 'vado ad aggiornare',
238
+ 'vado a configurare',
239
+ 'vado a correggere',
240
+ 'vado a creare',
241
+ 'vado ad aggiungere',
242
+ 'vado ad applicare',
243
+ 'fammi aggiornare',
244
+ 'fammi correggere',
197
245
  ]
198
246
 
199
247
  // Portuguese: "vou" + work verb; "deixa eu" idioms.
@@ -214,6 +262,16 @@ const PT_PHRASES: readonly string[] = [
214
262
  'um momento',
215
263
  'me dê um momento',
216
264
  'me dá um segundo',
265
+ // Action/config verb family.
266
+ 'vou atualizar',
267
+ 'vou configurar',
268
+ 'vou corrigir',
269
+ 'vou criar',
270
+ 'vou adicionar',
271
+ 'vou agendar',
272
+ 'vou aplicar',
273
+ 'deixa eu atualizar',
274
+ 'deixa eu corrigir',
217
275
  ]
218
276
 
219
277
  // German: "ich werde" / "lass mich" + work verb; "ich schaue gleich" idioms.
@@ -238,10 +296,22 @@ const DE_PHRASES: readonly string[] = [
238
296
  'einen moment',
239
297
  'einen augenblick',
240
298
  'gib mir eine sekunde',
299
+ // Action/config verb family.
300
+ 'ich werde aktualisieren',
301
+ 'ich werde konfigurieren',
302
+ 'ich werde korrigieren',
303
+ 'ich werde einrichten',
304
+ 'ich werde erstellen',
305
+ 'ich werde hinzufügen',
306
+ 'ich werde anwenden',
307
+ 'lass mich aktualisieren',
308
+ 'lass mich korrigieren',
241
309
  ]
242
310
 
243
311
  // Russian: first-person-future verbs (проверю/посмотрю/продолжу) — the -ю/-у
244
- // inflection is unambiguously "I will", so it is a safe self-anchor.
312
+ // inflection is unambiguously "I will", so it is a safe self-anchor. (Note: the
313
+ // bare -ю ending is shared with present-imperfective "я делаю" = "I do", so this
314
+ // stays an enumerated list rather than a morpheme regex.)
245
315
  const RU_PHRASES: readonly string[] = [
246
316
  'сейчас проверю',
247
317
  'я проверю',
@@ -254,6 +324,15 @@ const RU_PHRASES: readonly string[] = [
254
324
  'дайте мне минуту',
255
325
  'одну секунду',
256
326
  'минутку',
327
+ // Action/config verb family (perfective first-person futures).
328
+ 'я обновлю',
329
+ 'я настрою',
330
+ 'я исправлю',
331
+ 'я создам',
332
+ 'я добавлю',
333
+ 'я применю',
334
+ 'сейчас обновлю',
335
+ 'сейчас исправлю',
257
336
  ]
258
337
 
259
338
  // Chinese: 我会/我来/我再 + work verb. Full multi-character intent phrases only;
@@ -277,26 +356,39 @@ const ZH_PHRASES: readonly string[] = [
277
356
  '让我检查一下',
278
357
  '稍等一下',
279
358
  '我看一下',
359
+ // Action/config verb family.
360
+ '我来更新',
361
+ '我会更新',
362
+ '我来配置',
363
+ '我来设置',
364
+ '我会设置',
365
+ '我来修改',
366
+ '我会修改',
367
+ '我来修复',
368
+ '我来创建',
369
+ '我来添加',
370
+ '我马上更新',
371
+ '让我更新',
372
+ '让我改一下',
280
373
  ]
281
374
 
282
- // Japanese: -てみます / -します first-person volitional on check/look/continue.
283
- // Bare nouns (確認) are excluded; the verb ending carries the self-direction.
375
+ // Japanese: handled by the JA_VOLITIONAL morpheme check below (-します/-いたします/
376
+ // -してみます generalizes across all する action verbs). Only the regular-verb
377
+ // ます forms (調べます/見てみます/続けます — bare ます is too broad to regex) and
378
+ // stall idioms are enumerated here.
284
379
  const JA_PHRASES: readonly string[] = [
285
- '確認します',
286
- '確認してみます',
287
- '確認いたします',
288
380
  '調べてみます',
289
381
  '調べます',
290
382
  '見てみます',
291
383
  '続けます',
292
- '引き続き確認します',
293
- 'すぐ確認します',
294
384
  '少々お待ちください',
295
385
  'ちょっと待ってください',
296
386
  ]
297
387
 
298
388
  // Arabic: future particle سـ prefixed first-person verb (سأتحقق = "I will
299
- // verify"). The سأ prefix is unambiguously first-person-future.
389
+ // verify"). The سأ prefix is first-person-future, but as a bare substring it
390
+ // collides with the root س-أ-ل ("ask": سألت "I asked", المسألة "the matter"), so
391
+ // this stays an enumerated list of full verbs rather than a سأ-prefix regex.
300
392
  const AR_PHRASES: readonly string[] = [
301
393
  'سأتحقق',
302
394
  'سأتأكد',
@@ -307,30 +399,31 @@ const AR_PHRASES: readonly string[] = [
307
399
  'دعني أتحقق',
308
400
  'دعني أراجع',
309
401
  'لحظة من فضلك',
402
+ // Action/config verb family.
403
+ 'سأحدث',
404
+ 'سأحدّث',
405
+ 'سأعدل',
406
+ 'سأعدّل',
407
+ 'سأضبط',
408
+ 'سأصلح',
409
+ 'سأنشئ',
410
+ 'سأضيف',
310
411
  ]
311
412
 
312
- // Hindi: first-person-future "मैं करूँगा/देखूँगा" forms (multi-word so they
313
- // cannot collide with a bare common word).
314
- const HI_PHRASES: readonly string[] = [
315
- 'जाँच करूँगा',
316
- 'जांच करूंगा',
317
- 'देख लूँगा',
318
- 'देख लूंगा',
319
- 'जारी रखूँगा',
320
- 'जारी रखूंगा',
321
- 'एक मिनट रुकिए',
322
- ]
413
+ // Hindi: first-person-future is the -ūṅgā/-ūṅgī suffix, matched by
414
+ // MORPHEME_PATTERNS below (it covers all X-करना compounds). Only the stall idiom
415
+ // is enumerated here.
416
+ const HI_PHRASES: readonly string[] = ['एक मिनट रुकिए']
323
417
 
324
- // Turkish: first-person-future "-eceğim/-acağım" on check/look/continue verbs.
418
+ // Turkish: first-person-future "-eceğim/-acağım" is matched by MORPHEME_PATTERNS
419
+ // below. The present-progressive ("ediyorum" = "I'm checking now"), optative
420
+ // ("bir bakayım" = "let me look"), and stall idioms stay enumerated — the
421
+ // progressive -ıyorum ending is too polysemous to regex ("biliyorum" = "I know").
325
422
  const TR_PHRASES: readonly string[] = [
326
- 'kontrol edeceğim',
327
423
  'kontrol ediyorum',
328
- 'bakacağım',
329
424
  'bir bakayım',
330
425
  'bir kontrol edeyim',
331
426
  'kontrol edeyim',
332
- 'inceleyeceğim',
333
- 'devam edeceğim',
334
427
  'hemen kontrol ediyorum',
335
428
  'hemen bakıyorum',
336
429
  'bir saniye',
@@ -348,6 +441,14 @@ const VI_PHRASES: readonly string[] = [
348
441
  'tôi xem ngay',
349
442
  'chờ một chút',
350
443
  'đợi một chút',
444
+ // Action/config verb family.
445
+ 'tôi sẽ cập nhật',
446
+ 'tôi sẽ cấu hình',
447
+ 'tôi sẽ sửa',
448
+ 'tôi sẽ tạo',
449
+ 'tôi sẽ thêm',
450
+ 'để tôi cập nhật',
451
+ 'để tôi sửa',
351
452
  ]
352
453
 
353
454
  // Indonesian: "saya akan" / "biar saya" (I will / let me) + work verb.
@@ -362,6 +463,16 @@ const ID_PHRASES: readonly string[] = [
362
463
  'saya periksa dulu',
363
464
  'tunggu sebentar',
364
465
  'sebentar ya',
466
+ // Action/config verb family.
467
+ 'saya akan perbarui',
468
+ 'saya akan memperbarui',
469
+ 'saya akan atur',
470
+ 'saya akan konfigurasi',
471
+ 'saya akan perbaiki',
472
+ 'saya akan buat',
473
+ 'saya akan tambah',
474
+ 'biar saya perbaiki',
475
+ 'biar saya perbarui',
365
476
  ]
366
477
 
367
478
  const ALL_PHRASES: readonly string[] = [
@@ -382,6 +493,41 @@ const ALL_PHRASES: readonly string[] = [
382
493
  ...ID_PHRASES,
383
494
  ]
384
495
 
496
+ // First-person future/volitional realized as a verb inflection or affix (not a
497
+ // separate word), so matching the marker generalizes across ALL action verbs.
498
+ const MORPHEME_PATTERNS: readonly RegExp[] = [
499
+ // Korean first-person volitional: a verb stem + -겠습니다/-겠어요. The stem must
500
+ // be one of the action/auxiliary verbs 하 (the 하다 light verb behind every
501
+ // X하다 — 업데이트하겠습니다/반영하겠어요), 보 (살펴보겠습니다), 두 (반영해두겠습니다), or
502
+ // 놓 (해놓겠습니다). Anchoring on the verb stem is what keeps this self-directed:
503
+ // bare 겠 also matches adjective-stem CONJECTURE (좋겠어요 "that'd be nice",
504
+ // 괜찮겠어요 "must be fine") and the idioms 알겠/모르겠, none of which promise work.
505
+ // Listener-directed conjecture takes the honorific 시 (피곤하시겠어요 → 시겠, not
506
+ // 하겠), so it is excluded too.
507
+ /(?:하|보|두|놓)겠(?:습니다|어요)/,
508
+ // Turkish first-person-singular future "-acağım/-eceğim" ("I will VERB").
509
+ // Vowel harmony yields exactly these two suffixes; the y-buffer
510
+ // ("bekleyeceğim") leaves the suffix intact.
511
+ /acağım|eceğim/,
512
+ // Hindi first-person future "-ūṅgā/-ūṅgī" on any verb, covering all X-करना
513
+ // compounds ("अपडेट करूँगा"). Both nasal spellings (ँ U+0901 / ं U+0902) and
514
+ // both genders (ा/ी) are included.
515
+ /ू[ँं]ग[ाी]/,
516
+ ]
517
+
518
+ // Japanese する-verb volitional します/いたします/してみます — covers the action class
519
+ // (更新します/設定します/対応します). Bare ます is the universal polite verb ending and
520
+ // far too broad to match, so this keys on します specifically. Two request/greeting
521
+ // idioms end in します without being work intent — お願い(いた)します ("please") and
522
+ // 失礼します ("excuse me") — and are stripped before the test so they don't fire.
523
+ // The (?![か??]) lookahead drops question forms — both the か question particle
524
+ // (どうしますか "what should I do?") and a trailing question mark, fullwidth ? or
525
+ // ASCII ? (どうします?, 更新します? "shall I update?"). A question awaits the user;
526
+ // it is not a commitment to act this turn. A statement keeps its 。/, so
527
+ // 更新します。 still matches.
528
+ const JA_VOLITIONAL_IDIOMS = /お願い(?:いた)?します|失礼します/g
529
+ const JA_VOLITIONAL = /します(?![か??])|してみます(?![か??])/
530
+
385
531
  // Reply texts shorter than this are almost always a complete final answer
386
532
  // ("네", "ok", "done") where a partial match would be noise. The shortest
387
533
  // legitimate intent phrases ("on it now", "확인할게요") clear this floor.
@@ -391,5 +537,7 @@ export function detectContinuationWillingness(text: string): boolean {
391
537
  if (text.length < MIN_LENGTH) return false
392
538
  const normalized = normalize(text)
393
539
  if (normalized.length < MIN_LENGTH) return false
394
- return ALL_PHRASES.some((phrase) => normalized.includes(phrase))
540
+ if (ALL_PHRASES.some((phrase) => normalized.includes(phrase))) return true
541
+ if (MORPHEME_PATTERNS.some((pattern) => pattern.test(normalized))) return true
542
+ return JA_VOLITIONAL.test(normalized.replace(JA_VOLITIONAL_IDIOMS, ''))
395
543
  }