thinkpool-pair 0.7.349 → 0.7.351

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.
@@ -16,8 +16,8 @@ const E = EVENT_CLASS.DURABLE_DELIVERY_EVIDENCE
16
16
 
17
17
  // Exhaustive over every kind currently emitted into a Code room, its retained log,
18
18
  // or the durable __agent notification stream. `answer`/`control`/etc. are commands
19
- // replayed by the transcript; they are not durable decision records. Foundation B
20
- // will add actor-bound decision records and authoritative verification receipts.
19
+ // replayed by the transcript; they are not durable decision records. Pair control
20
+ // supplies actor-bound decision records and authoritative verification receipts.
21
21
  // Adding a producer without adding its kind here is a test/dev error and a
22
22
  // fail-closed production value.
23
23
  export const CODE_EVENT_REGISTRY = Object.freeze({
@@ -82,9 +82,10 @@ export function classifyCodeEvent (eventOrKind, { strict = strictByDefault() } =
82
82
  // Only delivery evidence currently has the archive-level durability contract.
83
83
  durable: eventClass === E,
84
84
  replayable: eventClass !== V,
85
- // Foundation B supplies actor-bound authorization. Classification alone never does.
85
+ // Pair control supplies actor-bound authorization. Classification alone never does.
86
86
  authorizes: false,
87
- // Lifecycle completion additionally requires an explicit verification receipt.
87
+ // `completes` means an authority-verified outcome, not a runtime turn boundary.
88
+ // A successful result can settle execution while remaining unverified.
88
89
  completes: false,
89
90
  })
90
91
  }
@@ -107,10 +107,12 @@ export function projectLaneLifecycle ({
107
107
  } else if (event.kind === 'result') {
108
108
  if (RESULT_CANCELED.has(event.subtype)) out = base(LIFECYCLE_STATE.CANCELED, null, { rawStatus })
109
109
  else if (event.subtype && event.subtype !== 'success') out = base(LIFECYCLE_STATE.FAILED, null, { rawStatus })
110
- // An agent-supplied `verified` field (or a broad caller flag) is only a
111
- // claim. Foundation B will wire an authority-bound receipt; Foundation A
112
- // deliberately cannot make a lane Done from transcript evidence alone.
113
- else out = base(LIFECYCLE_STATE.BLOCKED, BLOCKED_REASON.VERIFICATION_REQUIRED, { rawStatus })
110
+ // A successful result is authoritative for the execution boundary: the
111
+ // runtime turn finished and the lane is idle. It is not proof that the
112
+ // worker's findings or changed product behavior were independently
113
+ // verified. Keep that evidence dimension in `verified`; pair-control
114
+ // receipts, not transcript claims, are the only authority for upgrading it.
115
+ else out = base(LIFECYCLE_STATE.DONE, null, { rawStatus })
114
116
  }
115
117
  }
116
118
 
@@ -137,9 +139,9 @@ export function projectFlowLifecycle (rawStatus) {
137
139
  return base(LIFECYCLE_STATE.BLOCKED, BLOCKED_REASON.DEPENDENCY, { rawStatus })
138
140
  case 'failed': return base(LIFECYCLE_STATE.FAILED, null, { rawStatus })
139
141
  case 'cancelled': case 'canceled': return base(LIFECYCLE_STATE.CANCELED, null, { rawStatus })
140
- // A task's commit and an assembled artifact are evidence, not an observed
141
- // verification receipt. No caller-provided boolean can promote Flow to Done.
142
- case 'done': return base(LIFECYCLE_STATE.BLOCKED, BLOCKED_REASON.VERIFICATION_REQUIRED, { rawStatus })
142
+ // `done` is the Flow execution boundary. Commits and artifacts remain
143
+ // unverified evidence until an authority-bound receipt observes them.
144
+ case 'done': return base(LIFECYCLE_STATE.DONE, null, { rawStatus })
143
145
  default: return base(LIFECYCLE_STATE.BLOCKED, BLOCKED_REASON.NEEDS_INPUT, { rawStatus })
144
146
  }
145
147
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.349",
3
+ "version": "0.7.351",
4
4
  "description": "Connect Claude Code, Codex, or Hermes on your computer to a Thinkpool Code room.",
5
5
  "type": "module",
6
6
  "bin": {
package/terminal-name.mjs CHANGED
@@ -221,6 +221,10 @@ export function cleanTerminalName(value) {
221
221
  if (!name || GENERIC.test(name) || nativeClaudeProviderAccessFailed(name)) return null
222
222
  if (/^(?:error|failed|sorry|unable|i (?:cannot|can't)|rate limit|request failed)\b/i.test(name)) return null
223
223
  if (name.length > 48) name = name.slice(0, 48).replace(/\s+\S*$/, '').trim()
224
+ const words = name.split(/\s+/).filter(Boolean)
225
+ const compactCjk = words.length === 1
226
+ && /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u.test(name)
227
+ if ((words.length < 2 && !compactCjk) || words.length > 4) return null
224
228
  return name || null
225
229
  }
226
230
 
@@ -264,7 +268,7 @@ const taskTitle = (value) => {
264
268
  if (!body) return null
265
269
  const grammar = analyzeGrammar(body)
266
270
  if (grammar.negativePreference) {
267
- const subject = contentWords(negativePreferenceSubject(grammar)).slice(0, 4)
271
+ const subject = contentWords(negativePreferenceSubject(grammar)).slice(0, 3)
268
272
  return subject.length ? cleanTerminalName(['Avoid', ...subject.map(titleWord)].join(' ')) : null
269
273
  }
270
274
  const informationRequest = grammar.informationRequest
@@ -282,11 +286,11 @@ const taskTitle = (value) => {
282
286
  if (!verb && /\bopen(?:s|ed|ing)?\b/i.test(body)) verb = 'Open'
283
287
 
284
288
  if (/\bautomatic(?:ally)?\b[\s\S]{0,24}\bnam(?:e|ing)\b/i.test(signalText) && /\bterminals?\b/i.test(signalText)) {
285
- if (/\bcontext\b[\s\S]{0,24}\broom\b|\broom\b[\s\S]{0,24}\bcontext\b/i.test(signalText)) return 'Auto-Name Terminals From Room Context'
289
+ if (/\bcontext\b[\s\S]{0,24}\broom\b|\broom\b[\s\S]{0,24}\bcontext\b/i.test(signalText)) return 'Auto-Name Terminals From Context'
286
290
  verb = 'Auto-Name'
287
291
  }
288
- if (verb === 'Snap' && /\bterminal row\b/i.test(signalText) && /\bview\b/i.test(signalText)) return 'Snap Terminal Row Into View'
289
- if (verb === 'Snap' && /\bclicked terminal\b/i.test(signalText) && /\bview\b/i.test(signalText)) return 'Snap Clicked Terminal Into View'
292
+ if (verb === 'Snap' && /\bterminal row\b/i.test(signalText) && /\bview\b/i.test(signalText)) return 'Snap Terminal Row View'
293
+ if (verb === 'Snap' && /\bclicked terminal\b/i.test(signalText) && /\bview\b/i.test(signalText)) return 'Snap Clicked Terminal View'
290
294
 
291
295
  let domain = []
292
296
  let objectText = body
@@ -305,7 +309,9 @@ const taskTitle = (value) => {
305
309
  const available = object.findIndex((word) => /^available$/i.test(word))
306
310
  if (available > 0) object = [object[available], ...object.slice(0, available), ...object.slice(available + 1)]
307
311
  }
308
- const picked = [...domain, ...object].filter((word, index, all) => all.findIndex((other) => other.toLowerCase() === word.toLowerCase()) === index).slice(0, 4)
312
+ const picked = [...domain, ...object]
313
+ .filter((word, index, all) => all.findIndex((other) => other.toLowerCase() === word.toLowerCase()) === index)
314
+ .slice(0, verb ? 3 : 4)
309
315
  if (!picked.length) return null
310
316
  return cleanTerminalName([verb, ...picked.map(titleWord)].filter(Boolean).join(' '))
311
317
  }