@cocorograph/hub-agent 0.8.35 → 0.8.37
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 +1 -1
- package/src/codex-stream-bridge.mjs +114 -4
- package/src/main.mjs +1 -0
package/package.json
CHANGED
|
@@ -390,6 +390,20 @@ function subagentFallbackName(agentPath) {
|
|
|
390
390
|
return agentPath.split("/").filter(Boolean).at(-1) || "subagent"
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
+
function explicitSubagentRoleFromThread(thread, expectedParentThreadId) {
|
|
394
|
+
const threadSpawn = thread?.source?.subAgent?.thread_spawn
|
|
395
|
+
if (
|
|
396
|
+
threadSpawn &&
|
|
397
|
+
Object.prototype.hasOwnProperty.call(threadSpawn, "parent_thread_id") &&
|
|
398
|
+
threadSpawn.parent_thread_id !== expectedParentThreadId
|
|
399
|
+
) return null
|
|
400
|
+
const directRole = typeof thread?.agentRole === "string" ? thread.agentRole.trim() : ""
|
|
401
|
+
if (directRole) return directRole
|
|
402
|
+
const sourceRole = threadSpawn?.agent_role
|
|
403
|
+
const normalizedSourceRole = typeof sourceRole === "string" ? sourceRole.trim() : ""
|
|
404
|
+
return normalizedSourceRole || null
|
|
405
|
+
}
|
|
406
|
+
|
|
393
407
|
function parseDynamicToolArguments(value) {
|
|
394
408
|
if (value && typeof value === "object" && !Array.isArray(value)) return value
|
|
395
409
|
if (typeof value !== "string") return null
|
|
@@ -749,10 +763,16 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
749
763
|
* deliveryPromise: Promise<object>, settleDelivery: Function}>} */
|
|
750
764
|
pendingInjectSources: [],
|
|
751
765
|
/** 組み込み collaboration subagent の表示用台帳。検索キーは agentThreadId、
|
|
752
|
-
* 外部へ出す安定した委譲 ID は
|
|
766
|
+
* 外部へ出す安定した委譲 ID は subAgentActivity の lifecycle ID を保持する。 */
|
|
753
767
|
subagents: new Map(),
|
|
754
768
|
/** dynamicToolCall(spawn_agent) の item.id -> 表示メタデータ。 */
|
|
755
769
|
subagentSpawnMetadata: new Map(),
|
|
770
|
+
/** live subAgentActivity の spawn metadata miss を child thread/read で補完する
|
|
771
|
+
* single-flight 台帳。key = agentThreadId, value = 進行中 Promise。 */
|
|
772
|
+
subagentRoleReads: new Map(),
|
|
773
|
+
/** role 補完を試行済みの child thread。取得不能・親不一致・RPC失敗後の後続 activity で
|
|
774
|
+
* 無制限に read/warn を再発火しない negative cache(session と同じ寿命)。 */
|
|
775
|
+
subagentRoleReadAttempted: new Set(),
|
|
756
776
|
/** 実行中の背景コマンド(commandExecution)の台帳。claude 側 `_liveTasks` と同型。
|
|
757
777
|
* key = `item.id`(`item/started` / `item/completed` の両方に載る唯一の安定 ID)、
|
|
758
778
|
* value = { threadId, startedAt, lastSeenAt }。
|
|
@@ -1593,6 +1613,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1593
1613
|
agentPath: record.agentPath,
|
|
1594
1614
|
phase: record.phase,
|
|
1595
1615
|
agent: record.agent,
|
|
1616
|
+
agentExplicit: record.agentExplicit === true,
|
|
1596
1617
|
description: record.description,
|
|
1597
1618
|
prompt: record.prompt,
|
|
1598
1619
|
background: record.background === true,
|
|
@@ -1636,6 +1657,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1636
1657
|
agentPath,
|
|
1637
1658
|
phase,
|
|
1638
1659
|
agent: metadata?.agent || previous?.agent || subagentFallbackName(agentPath),
|
|
1660
|
+
agentExplicit: metadata?.agentExplicit === true || previous?.agentExplicit === true,
|
|
1639
1661
|
description:
|
|
1640
1662
|
metadata?.description || previous?.description || subagentFallbackName(agentPath),
|
|
1641
1663
|
prompt: metadata?.prompt || previous?.prompt || "",
|
|
@@ -1654,6 +1676,80 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1654
1676
|
return record
|
|
1655
1677
|
}
|
|
1656
1678
|
|
|
1679
|
+
_recoverLiveSubagentRole(session, record, itemId) {
|
|
1680
|
+
if (!session || !record || record.agentExplicit === true || session.dead) return
|
|
1681
|
+
const agentThreadId = record.agentThreadId
|
|
1682
|
+
if (
|
|
1683
|
+
session.subagentRoleReads.has(agentThreadId) ||
|
|
1684
|
+
session.subagentRoleReadAttempted.has(agentThreadId)
|
|
1685
|
+
) return
|
|
1686
|
+
session.subagentRoleReadAttempted.add(agentThreadId)
|
|
1687
|
+
this.logger?.warn?.(
|
|
1688
|
+
{
|
|
1689
|
+
item_id: itemId,
|
|
1690
|
+
parent_thread_id: session.threadId,
|
|
1691
|
+
subagent_thread_id: agentThreadId,
|
|
1692
|
+
},
|
|
1693
|
+
"codex live subagent spawn metadata miss; recovering role from child thread",
|
|
1694
|
+
)
|
|
1695
|
+
const attempt = (async () => {
|
|
1696
|
+
let result
|
|
1697
|
+
try {
|
|
1698
|
+
result = await session.client.request("thread/read", {
|
|
1699
|
+
threadId: agentThreadId,
|
|
1700
|
+
includeTurns: false,
|
|
1701
|
+
})
|
|
1702
|
+
} catch {
|
|
1703
|
+
this.logger?.warn?.(
|
|
1704
|
+
{
|
|
1705
|
+
item_id: itemId,
|
|
1706
|
+
parent_thread_id: session.threadId,
|
|
1707
|
+
subagent_thread_id: agentThreadId,
|
|
1708
|
+
},
|
|
1709
|
+
"codex live subagent role recovery thread/read failed",
|
|
1710
|
+
)
|
|
1711
|
+
return
|
|
1712
|
+
}
|
|
1713
|
+
if (session.dead) return
|
|
1714
|
+
const current = session.subagents.get(agentThreadId)
|
|
1715
|
+
if (!current || current.agentExplicit === true) return
|
|
1716
|
+
const explicitRole = explicitSubagentRoleFromThread(result?.thread, session.threadId)
|
|
1717
|
+
if (!explicitRole) return
|
|
1718
|
+
const updated = this._setSubagentPhase(
|
|
1719
|
+
session,
|
|
1720
|
+
agentThreadId,
|
|
1721
|
+
current.phase,
|
|
1722
|
+
{
|
|
1723
|
+
agentPath: current.agentPath,
|
|
1724
|
+
metadata: { agent: explicitRole, agentExplicit: true },
|
|
1725
|
+
atMs: current.atMs,
|
|
1726
|
+
},
|
|
1727
|
+
{ force: true },
|
|
1728
|
+
)
|
|
1729
|
+
// 成功後は record.agentExplicit が再試行を止めるため negative cache は不要。
|
|
1730
|
+
if (updated?.agentExplicit === true) {
|
|
1731
|
+
session.subagentRoleReadAttempted.delete(agentThreadId)
|
|
1732
|
+
}
|
|
1733
|
+
})()
|
|
1734
|
+
const pending = attempt
|
|
1735
|
+
.catch(() => {
|
|
1736
|
+
this.logger?.warn?.(
|
|
1737
|
+
{
|
|
1738
|
+
item_id: itemId,
|
|
1739
|
+
parent_thread_id: session.threadId,
|
|
1740
|
+
subagent_thread_id: agentThreadId,
|
|
1741
|
+
},
|
|
1742
|
+
"codex live subagent role recovery failed",
|
|
1743
|
+
)
|
|
1744
|
+
})
|
|
1745
|
+
.finally(() => {
|
|
1746
|
+
if (session.subagentRoleReads.get(agentThreadId) === pending) {
|
|
1747
|
+
session.subagentRoleReads.delete(agentThreadId)
|
|
1748
|
+
}
|
|
1749
|
+
})
|
|
1750
|
+
session.subagentRoleReads.set(agentThreadId, pending)
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1657
1753
|
_observeSubagentItem(
|
|
1658
1754
|
session,
|
|
1659
1755
|
item,
|
|
@@ -1673,8 +1769,11 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1673
1769
|
if (namespace !== "collaboration" || item.tool !== "spawn_agent" || !item.id) return
|
|
1674
1770
|
const args = parseDynamicToolArguments(item.arguments)
|
|
1675
1771
|
if (!args) return
|
|
1772
|
+
const agent = typeof args.agent_type === "string" ? args.agent_type.trim() : ""
|
|
1773
|
+
const agentExplicit = agent.length > 0
|
|
1676
1774
|
const metadata = {
|
|
1677
|
-
agent:
|
|
1775
|
+
agent: agentExplicit ? agent : null,
|
|
1776
|
+
agentExplicit,
|
|
1678
1777
|
description: typeof args.task_name === "string" && args.task_name ? args.task_name : null,
|
|
1679
1778
|
prompt: typeof args.message === "string" ? args.message : null,
|
|
1680
1779
|
background: args.background === true,
|
|
@@ -1697,7 +1796,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1697
1796
|
}
|
|
1698
1797
|
if (!phase) return
|
|
1699
1798
|
const metadata = session.subagentSpawnMetadata.get(item.id) || null
|
|
1700
|
-
this._setSubagentPhase(
|
|
1799
|
+
const record = this._setSubagentPhase(
|
|
1701
1800
|
session,
|
|
1702
1801
|
agentThreadId,
|
|
1703
1802
|
phase,
|
|
@@ -1709,6 +1808,11 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1709
1808
|
},
|
|
1710
1809
|
{ emit },
|
|
1711
1810
|
)
|
|
1811
|
+
// metadata が存在して agentExplicit=false なら agent_type 未指定という明示入力。
|
|
1812
|
+
// thread/read 補完は metadata lookup 自体が外れた場合だけに限定し、その指定を上書きしない。
|
|
1813
|
+
if (emit && record && !metadata) {
|
|
1814
|
+
this._recoverLiveSubagentRole(session, record, item.id)
|
|
1815
|
+
}
|
|
1712
1816
|
}
|
|
1713
1817
|
|
|
1714
1818
|
_observeSubagentTurn(session, msg) {
|
|
@@ -1769,6 +1873,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1769
1873
|
agentThreadId: record.agentThreadId,
|
|
1770
1874
|
agentPath: record.agentPath,
|
|
1771
1875
|
agent: record.agent,
|
|
1876
|
+
agentExplicit: record.agentExplicit === true,
|
|
1772
1877
|
description: record.description,
|
|
1773
1878
|
prompt: record.prompt,
|
|
1774
1879
|
background: record.background === true,
|
|
@@ -1802,6 +1907,10 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1802
1907
|
// RPC 待ち中に live lifecycle が進んだ場合、stale read で上書き・削除しない。
|
|
1803
1908
|
if (session.subagents.get(record.agentThreadId) !== record) continue
|
|
1804
1909
|
const thread = result?.thread
|
|
1910
|
+
const explicitRole = explicitSubagentRoleFromThread(thread, session.threadId)
|
|
1911
|
+
const metadata = explicitRole
|
|
1912
|
+
? { agent: explicitRole, agentExplicit: true }
|
|
1913
|
+
: null
|
|
1805
1914
|
const turns = Array.isArray(thread?.turns) ? thread.turns : []
|
|
1806
1915
|
const lastTurn = turns.at(-1)
|
|
1807
1916
|
const phase = terminalSubagentPhase(lastTurn?.status)
|
|
@@ -1816,7 +1925,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1816
1925
|
session,
|
|
1817
1926
|
record.agentThreadId,
|
|
1818
1927
|
phase,
|
|
1819
|
-
{ agentPath: record.agentPath, atMs: lastTurnAtMs },
|
|
1928
|
+
{ agentPath: record.agentPath, metadata, atMs: lastTurnAtMs },
|
|
1820
1929
|
{ emit: false },
|
|
1821
1930
|
)
|
|
1822
1931
|
} else if (
|
|
@@ -1829,6 +1938,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1829
1938
|
"started",
|
|
1830
1939
|
{
|
|
1831
1940
|
agentPath: record.agentPath,
|
|
1941
|
+
metadata,
|
|
1832
1942
|
atMs: record.startedAtMs,
|
|
1833
1943
|
},
|
|
1834
1944
|
{ emit: false },
|
package/src/main.mjs
CHANGED
|
@@ -2076,6 +2076,7 @@ export function buildCodexReadyPayload(streamId, info, queueState) {
|
|
|
2076
2076
|
agent_thread_id: subagent.agentThreadId,
|
|
2077
2077
|
agent_path: subagent.agentPath,
|
|
2078
2078
|
agent: subagent.agent,
|
|
2079
|
+
agent_explicit: subagent.agentExplicit === true,
|
|
2079
2080
|
// description は spawn_agent.task_name の既存 alias(人物の表示名ではない)。
|
|
2080
2081
|
description: subagent.description,
|
|
2081
2082
|
prompt: subagent.prompt,
|