peertable 0.8.42 → 0.8.44

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peertable",
3
- "version": "0.8.42",
3
+ "version": "0.8.44",
4
4
  "description": "A round table of peer agents. No orchestrator at the head. Turn Claude Code, Codex, and Grok sessions into a team of equal, long-lived peers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/room/client.mjs CHANGED
@@ -14,7 +14,7 @@ import { boundedRecent, boundedUnread } from './message-bounds.mjs'
14
14
 
15
15
  // client.mjs 側のハードコード版数。package.json の version と一致していることを
16
16
  // diagnostics の version_consistency が見る(2 つの版数源の drift 検出。決定45)
17
- const MCP_VERSION = '0.8.42'
17
+ const MCP_VERSION = '0.8.44'
18
18
  const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
19
19
 
20
20
  const USAGE = `usage:
@@ -37,8 +37,8 @@ def main(argv):
37
37
  lines = [
38
38
  f"# 円卓ログ — room `{room}`(全{len(messages)}発言)",
39
39
  "",
40
- "teardown(archive モード)が書き出した正史。room サーバー側は削除済みなので、"
41
- "この文書が唯一の記録である。",
40
+ "teardown(archive モード)が、解散の区切りを投稿する前までの room ログを書き出した控え。"
41
+ "room と過去ログの原本はサーバー側に残り、次の卓も同じ room で続く。",
42
42
  "",
43
43
  "---",
44
44
  "",
@@ -198,3 +198,31 @@ test('teardownはalarm-bridgeを停止してから.teamを削除する', () => {
198
198
  assert.ok(source.includes('miss "alarm-bridge 停止に失敗'))
199
199
  assert.ok(source.includes('-X DELETE "$url/api/$room/bridges"'))
200
200
  })
201
+
202
+ test('archive room logは解散区切り前の控えであり原本をroomに残すと明記する', () => {
203
+ const dir = mkdtempSync(join(tmpdir(), 'peertable-archive-room-log-'))
204
+ const out = join(dir, 'room-log.md')
205
+ const script = fileURLToPath(new URL('./archive-room-log.py', import.meta.url))
206
+ const fixture = JSON.stringify({
207
+ messages: [{ seq: 1, from: 'bell', to_names: ['all'], ts: '2026-08-30T00:00:00Z', body: '完了' }],
208
+ })
209
+ const python = [
210
+ 'import importlib.util, json, sys',
211
+ 'spec = importlib.util.spec_from_file_location("archive_room_log", sys.argv[1])',
212
+ 'module = importlib.util.module_from_spec(spec)',
213
+ 'spec.loader.exec_module(module)',
214
+ 'fixture = json.loads(sys.argv[3])',
215
+ 'module.fetch = lambda _url, _path: fixture',
216
+ 'raise SystemExit(module.main(["https://room.example", "factory", sys.argv[2]]))',
217
+ ].join('; ')
218
+ try {
219
+ const result = spawnSync('python3', ['-c', python, script, out, fixture], { encoding: 'utf8' })
220
+ assert.equal(result.status, 0, result.stderr)
221
+ const log = readFileSync(out, 'utf8')
222
+ assert.match(log, /解散の区切りを投稿する前までの room ログを書き出した控え/u)
223
+ assert.match(log, /room と過去ログの原本はサーバー側に残り、次の卓も同じ room で続く/u)
224
+ assert.doesNotMatch(log, /削除済み|唯一の記録/u)
225
+ } finally {
226
+ rmSync(dir, { recursive: true, force: true })
227
+ }
228
+ })
@@ -0,0 +1,36 @@
1
+ // 席TUIへの入力方言の**唯一の正本**。
2
+ //
3
+ // 各harness TUIは入力の作法が異なり(実被弾の履歴: Escapeを撃つとClaudeの実行中ターンが
4
+ // 死ぬ 2026-08-22 / 素打ち連打はCodexのcomposerを壊す 2026-08-29 / bracketed pasteの
5
+ // 終端コードはGrokへリテラル混入する 2026-08-30)、その違いを呼び出し側のAIやscriptに
6
+ // 覚えさせると、片方で検証した変更が他方を壊す事故が構造的に再発する(オーナー裁定
7
+ // 2026-08-30「その違いをAIに要求するのが間違い。ツール側がミスが起こらないように作れ」)。
8
+ //
9
+ // 方言はこの表だけに書く。呼び出し側はharness名を渡して結果を使うだけであり、
10
+ // 表に無いharnessは推測で送らず typed error で止まる——新harnessの追加は
11
+ // この表へ1行足すまで配達自体ができない、という形でミスを機械的に封じる。
12
+ export const HARNESS_INPUT_DIALECTS = Object.freeze({
13
+ // Codex CLI: bracketed paste対応(素打ち連打はpaste burst検知を壊すので必ずpaste経由)
14
+ codex: Object.freeze({ bracketed_paste: true }),
15
+ // Claude Code: bracketed paste対応
16
+ claude: Object.freeze({ bracketed_paste: true }),
17
+ // Grok Build: bracketed paste**非対応**(終端コード ^[[201~ がリテラル混入して固着する)
18
+ grok: Object.freeze({ bracketed_paste: false }),
19
+ })
20
+
21
+ export function seatInputDialect(harness) {
22
+ const dialect = HARNESS_INPUT_DIALECTS[harness]
23
+ if (dialect === undefined) {
24
+ const error = new Error(`SEAT_INPUT_HARNESS_UNKNOWN: 入力方言表に '${harness}' が無い。`
25
+ + 'skill/scripts/seat-input.mjs の HARNESS_INPUT_DIALECTS へ実測に基づく1行を追加するまで、この席へは配達できない')
26
+ error.code = 'SEAT_INPUT_HARNESS_UNKNOWN'
27
+ throw error
28
+ }
29
+ return dialect
30
+ }
31
+
32
+ /** tmux paste-buffer の argv を方言から組む。呼び出し側は -p の要否を知らない。 */
33
+ export function seatPasteArgs(harness, bufferName, target) {
34
+ const dialect = seatInputDialect(harness)
35
+ return ['paste-buffer', ...(dialect.bracketed_paste ? ['-p'] : []), '-d', '-b', bufferName, '-t', target]
36
+ }
@@ -461,11 +461,12 @@ export function subtreeCpuSeconds(psRows, rootPid, { includeChild = () => true,
461
461
  return total
462
462
  }
463
463
 
464
- // ---- 親番犬の「起こす価値」判定(2026-08-29 オーナー裁定「番犬が必要な時だけ呼べ」)----
465
- // 選別は親のターン(有料)でなく機械(無料)が行う。起こすのは親の行動が要るものだけ。
466
- const ROUTINE_WAIT_DM = /^\s*\[待機\]/u
464
+ // ---- 親番犬の「起こす価値」判定 ----
465
+ // 2026-08-29裁定「番犬が必要な時だけ呼べ」で[待機]DMを沈黙させていたが、
466
+ // 2026-08-30裁定「待機にお前が気付けないのが問題だ。待機宣言は届くようにしろ」で撤回。
467
+ // 親宛DMは待機宣言を含めて全件起こす。room全体発言だけは従来どおり選別する。
467
468
  export function parentWatchShouldNotify(message, roomUpdate) {
468
469
  const body = String(message?.body ?? '')
469
470
  if (roomUpdate) return /全タスク完了|\[オーナー宛/u.test(body)
470
- return !ROUTINE_WAIT_DM.test(body)
471
+ return true
471
472
  }
@@ -23,6 +23,7 @@ import { resolvePostToken, resolveSeatObservation, tmuxArgv } from './seat-usage
23
23
  import { keysForCodexPane } from './codex-dialog.mjs'
24
24
  import { BROADCAST_RECIPIENT, formatWakeNotice, isIdleSelfWake, isWakeupBridgeTarget, memberHarness, shouldDeferGrokWake } from './wakeup-delivery.mjs'
25
25
  import { bridgeRecordLive } from './bridge-record-live.mjs'
26
+ import { seatPasteArgs } from './seat-input.mjs'
26
27
 
27
28
  const run = promisify(execFile)
28
29
  const [proj, ...rest] = process.argv.slice(2)
@@ -513,13 +514,11 @@ async function wake(seat, msgs) {
513
514
  writeFileSync(pasteFile, text)
514
515
  try {
515
516
  await run('tmux', tmuxArgv(['load-buffer', '-b', pasteBuf, pasteFile], { socket: observation.socket }))
516
- // bracketed paste(-p)はCodexの素打ち破損対策に必須だが、Grok TUIは非対応で
517
- // 終端コード ^[[201~ がリテラルとして composer へ混入し submit 不能になる
518
- // (実被弾 2026-08-30)。paste自体の原子性は -p なしでも保たれる。
519
- const pasteArgs = memberHarness(member) === 'grok'
520
- ? ['paste-buffer', '-d', '-b', pasteBuf, '-t', observation.target]
521
- : ['paste-buffer', '-p', '-d', '-b', pasteBuf, '-t', observation.target]
522
- await run('tmux', tmuxArgv(pasteArgs, { socket: observation.socket }))
517
+ // 貼り方の方言(-pの要否等)は seat-input.mjs の方言表だけが知る。ここでは判断しない。
518
+ await run('tmux', tmuxArgv(
519
+ seatPasteArgs(memberHarness(member), pasteBuf, observation.target),
520
+ { socket: observation.socket },
521
+ ))
523
522
  } finally {
524
523
  try { unlinkSync(pasteFile) } catch { /* 一時fileの掃除失敗は配達判定に影響しない */ }
525
524
  }