@cocorograph/hub-agent 0.8.34 → 0.8.35

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": "@cocorograph/hub-agent",
3
- "version": "0.8.34",
3
+ "version": "0.8.35",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの Claude / Codex チャットと tmux セッションを中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -748,8 +748,8 @@ export class CodexStreamBridge extends EventEmitter {
748
748
  * deliveryId: string, sourceStreamId: string, deliveryPending: boolean,
749
749
  * deliveryPromise: Promise<object>, settleDelivery: Function}>} */
750
750
  pendingInjectSources: [],
751
- /** 組み込み collaboration subagent の表示用台帳。ライフサイクルの安定キーは
752
- * agentThreadId(item.id は dynamicToolCall とのメタデータ結合にだけ使う)。 */
751
+ /** 組み込み collaboration subagent の表示用台帳。検索キーは agentThreadId、
752
+ * 外部へ出す安定した委譲 ID は spawn dynamicToolCall の item.id を保持する。 */
753
753
  subagents: new Map(),
754
754
  /** dynamicToolCall(spawn_agent) の item.id -> 表示メタデータ。 */
755
755
  subagentSpawnMetadata: new Map(),
@@ -1460,6 +1460,12 @@ export class CodexStreamBridge extends EventEmitter {
1460
1460
  const list = session?.pendingInjectSources
1461
1461
  if (!list || !list.length) return null
1462
1462
  if (msg?.method !== "item/completed") return null
1463
+ const sourceThreadId = msg.params?.threadId
1464
+ if (
1465
+ typeof sourceThreadId === "string" &&
1466
+ sourceThreadId &&
1467
+ sourceThreadId !== session?.threadId
1468
+ ) return null
1463
1469
  const item = msg.params?.item
1464
1470
  if (!item || item.type !== "userMessage") return null
1465
1471
  const content = item.content
@@ -1582,13 +1588,14 @@ export class CodexStreamBridge extends EventEmitter {
1582
1588
  notification: {
1583
1589
  method: "cockpit/subagent_state",
1584
1590
  params: {
1585
- delegationId: record.agentThreadId,
1591
+ delegationId: record.delegationId,
1586
1592
  agentThreadId: record.agentThreadId,
1587
1593
  agentPath: record.agentPath,
1588
1594
  phase: record.phase,
1589
1595
  agent: record.agent,
1590
1596
  description: record.description,
1591
1597
  prompt: record.prompt,
1598
+ background: record.background === true,
1592
1599
  atMs: record.atMs,
1593
1600
  },
1594
1601
  },
@@ -1609,6 +1616,14 @@ export class CodexStreamBridge extends EventEmitter {
1609
1616
  ? details.agentPath
1610
1617
  : previous?.agentPath || ""
1611
1618
  const metadata = details.metadata || null
1619
+ const delegationId =
1620
+ previous?.delegationId ||
1621
+ (typeof details.delegationId === "string" && details.delegationId
1622
+ ? details.delegationId
1623
+ : null)
1624
+ // raw child thread ID を委譲 ID に代用しない。spawn lifecycle 由来の安定 ID が無い
1625
+ // 通知は台帳へ登録せず、後続 item も unknown として未タグのまま扱う。
1626
+ if (typeof delegationId !== "string" || !delegationId) return null
1612
1627
  const transitionAtMs =
1613
1628
  previous?.phase === phase
1614
1629
  ? previous.atMs
@@ -1616,6 +1631,7 @@ export class CodexStreamBridge extends EventEmitter {
1616
1631
  ? details.atMs
1617
1632
  : Date.now()
1618
1633
  const record = {
1634
+ delegationId,
1619
1635
  agentThreadId,
1620
1636
  agentPath,
1621
1637
  phase,
@@ -1638,8 +1654,20 @@ export class CodexStreamBridge extends EventEmitter {
1638
1654
  return record
1639
1655
  }
1640
1656
 
1641
- _observeSubagentItem(session, item, atMs = Date.now(), { emit = true } = {}) {
1657
+ _observeSubagentItem(
1658
+ session,
1659
+ item,
1660
+ atMs = Date.now(),
1661
+ { emit = true, sourceThreadId = session?.threadId } = {},
1662
+ ) {
1642
1663
  if (!item || typeof item !== "object") return
1664
+ // root thread の collaboration item だけが root subagent 台帳の正本。子 thread 内の
1665
+ // spawn/subAgentActivity(孫)を root の lifecycle/snapshot へ昇格しない。
1666
+ if (
1667
+ typeof sourceThreadId === "string" &&
1668
+ sourceThreadId &&
1669
+ sourceThreadId !== session?.threadId
1670
+ ) return
1643
1671
  if (item.type === "dynamicToolCall") {
1644
1672
  const namespace = item.namespace
1645
1673
  if (namespace !== "collaboration" || item.tool !== "spawn_agent" || !item.id) return
@@ -1674,6 +1702,7 @@ export class CodexStreamBridge extends EventEmitter {
1674
1702
  agentThreadId,
1675
1703
  phase,
1676
1704
  {
1705
+ delegationId: item.id,
1677
1706
  agentPath: item.agentPath,
1678
1707
  metadata,
1679
1708
  atMs,
@@ -1736,7 +1765,7 @@ export class CodexStreamBridge extends EventEmitter {
1736
1765
  const startedHistoryIndex = startedIndexByThread.get(record.agentThreadId)
1737
1766
  if (startedHistoryIndex === undefined) continue
1738
1767
  snapshots.push({
1739
- delegationId: record.agentThreadId,
1768
+ delegationId: record.delegationId,
1740
1769
  agentThreadId: record.agentThreadId,
1741
1770
  agentPath: record.agentPath,
1742
1771
  agent: record.agent,
@@ -1880,25 +1909,46 @@ export class CodexStreamBridge extends EventEmitter {
1880
1909
  // ※ hub-agent 再起動 (cold resume) では historyItems が揮発するため青バブルへ戻る (claude の
1881
1910
  // jsonl at-rest と同じ許容範囲。source 永続マップは TTL/ID 再利用の複雑さに見合わないため不採用)。
1882
1911
  const eventThreadId = msg?.params?.threadId
1883
- const backgroundChildItemNotification =
1884
- typeof eventThreadId === "string" &&
1885
- typeof msg?.method === "string" &&
1886
- msg.method.startsWith("item/") &&
1887
- session.subagents.get(eventThreadId)?.background === true
1888
- if (backgroundChildItemNotification) {
1889
- // 背景子の item は root の履歴・バブルへ混ぜない。子自身の lifecycle は
1890
- // turn 通知と root 側 subAgentActivity が正本なので、子 item から孫を登録しない。
1891
- // foreground / background 不明(spawn record 生成前の短い窓を含む)は fail-open で
1892
- // 既存どおり root へ通す。背景出力の過剰抑止より既存表示の維持を優先する。
1912
+ const taggedParams = this._tagInjectSourceOnItem(session, msg)
1913
+ let effectiveParams = taggedParams || msg.params
1914
+ const childRecord =
1915
+ typeof eventThreadId === "string" ? session.subagents.get(eventThreadId) : null
1916
+ const childTextDelta =
1917
+ msg.method === "item/agentMessage/delta" ||
1918
+ msg.method === "item/reasoning/summaryTextDelta" ||
1919
+ msg.method === "item/reasoning/textDelta"
1920
+ if (childRecord && childTextDelta) {
1921
+ // 子の message/reasoning 途中 delta を root liveText へ混ぜない。前景・背景とも
1922
+ // attribution 付き item/completed が表示の正本で、root/unknown delta は従来どおり
1923
+ // fail-open で通す。
1893
1924
  return
1894
1925
  }
1895
- const taggedParams = this._tagInjectSourceOnItem(session, msg)
1896
- const effectiveParams = taggedParams || msg.params
1926
+ if (
1927
+ childRecord &&
1928
+ (msg.method === "item/started" || msg.method === "item/completed") &&
1929
+ effectiveParams?.item &&
1930
+ typeof effectiveParams.item === "object"
1931
+ ) {
1932
+ // app-server 所有の item は変更せず、既知の子 thread だけを attribution 付きで複製する。
1933
+ // unknown/root を推測で子扱いしない。delegationId は child thread ID ではなく、spawn
1934
+ // lifecycle が保持する安定 ID を使う。
1935
+ const attributedItem = {
1936
+ ...effectiveParams.item,
1937
+ _hubSubagent: {
1938
+ delegationId: childRecord.delegationId,
1939
+ threadId: eventThreadId,
1940
+ runtime: "codex",
1941
+ background: childRecord.background === true,
1942
+ },
1943
+ }
1944
+ effectiveParams = { ...effectiveParams, item: attributedItem }
1945
+ }
1897
1946
  if (msg.method === "item/started" || msg.method === "item/completed") {
1898
1947
  this._observeSubagentItem(
1899
1948
  session,
1900
1949
  effectiveParams?.item,
1901
1950
  effectiveParams?.completedAtMs || Date.now(),
1951
+ { sourceThreadId: eventThreadId },
1902
1952
  )
1903
1953
  }
1904
1954
  const childTurnNotification =
@@ -1968,8 +2018,8 @@ export class CodexStreamBridge extends EventEmitter {
1968
2018
  void this._pumpInputs(session)
1969
2019
  break
1970
2020
  case "item/completed":
1971
- // T06541 案1: 完了した ThreadItem を高忠実度履歴に追記する(cwd reattach で返す用)。
1972
- // Phase 2: タグ済み item (effectiveParams.item) を積む = reattach でも source が残る。
2021
+ // 完了した ThreadItem を高忠実度履歴に追記する(cwd reattach で返す用)。
2022
+ // attribution/source 済み item (effectiveParams.item) を積み、emit と同じ clone を使う。
1973
2023
  this._appendHistoryItem(session, effectiveParams?.item, effectiveParams?.completedAtMs)
1974
2024
  break
1975
2025
  default: