@cocorograph/hub-agent 0.8.34 → 0.8.36
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 +96 -21
- 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
|
+
(typeof threadSpawn.parent_thread_id !== "string" ||
|
|
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
|
|
@@ -748,8 +762,8 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
748
762
|
* deliveryId: string, sourceStreamId: string, deliveryPending: boolean,
|
|
749
763
|
* deliveryPromise: Promise<object>, settleDelivery: Function}>} */
|
|
750
764
|
pendingInjectSources: [],
|
|
751
|
-
/** 組み込み collaboration subagent
|
|
752
|
-
*
|
|
765
|
+
/** 組み込み collaboration subagent の表示用台帳。検索キーは agentThreadId、
|
|
766
|
+
* 外部へ出す安定した委譲 ID は subAgentActivity の lifecycle ID を保持する。 */
|
|
753
767
|
subagents: new Map(),
|
|
754
768
|
/** dynamicToolCall(spawn_agent) の item.id -> 表示メタデータ。 */
|
|
755
769
|
subagentSpawnMetadata: new Map(),
|
|
@@ -1460,6 +1474,12 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1460
1474
|
const list = session?.pendingInjectSources
|
|
1461
1475
|
if (!list || !list.length) return null
|
|
1462
1476
|
if (msg?.method !== "item/completed") return null
|
|
1477
|
+
const sourceThreadId = msg.params?.threadId
|
|
1478
|
+
if (
|
|
1479
|
+
typeof sourceThreadId === "string" &&
|
|
1480
|
+
sourceThreadId &&
|
|
1481
|
+
sourceThreadId !== session?.threadId
|
|
1482
|
+
) return null
|
|
1463
1483
|
const item = msg.params?.item
|
|
1464
1484
|
if (!item || item.type !== "userMessage") return null
|
|
1465
1485
|
const content = item.content
|
|
@@ -1582,13 +1602,15 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1582
1602
|
notification: {
|
|
1583
1603
|
method: "cockpit/subagent_state",
|
|
1584
1604
|
params: {
|
|
1585
|
-
delegationId: record.
|
|
1605
|
+
delegationId: record.delegationId,
|
|
1586
1606
|
agentThreadId: record.agentThreadId,
|
|
1587
1607
|
agentPath: record.agentPath,
|
|
1588
1608
|
phase: record.phase,
|
|
1589
1609
|
agent: record.agent,
|
|
1610
|
+
agentExplicit: record.agentExplicit === true,
|
|
1590
1611
|
description: record.description,
|
|
1591
1612
|
prompt: record.prompt,
|
|
1613
|
+
background: record.background === true,
|
|
1592
1614
|
atMs: record.atMs,
|
|
1593
1615
|
},
|
|
1594
1616
|
},
|
|
@@ -1609,6 +1631,14 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1609
1631
|
? details.agentPath
|
|
1610
1632
|
: previous?.agentPath || ""
|
|
1611
1633
|
const metadata = details.metadata || null
|
|
1634
|
+
const delegationId =
|
|
1635
|
+
previous?.delegationId ||
|
|
1636
|
+
(typeof details.delegationId === "string" && details.delegationId
|
|
1637
|
+
? details.delegationId
|
|
1638
|
+
: null)
|
|
1639
|
+
// raw child thread ID を委譲 ID に代用しない。spawn lifecycle 由来の安定 ID が無い
|
|
1640
|
+
// 通知は台帳へ登録せず、後続 item も unknown として未タグのまま扱う。
|
|
1641
|
+
if (typeof delegationId !== "string" || !delegationId) return null
|
|
1612
1642
|
const transitionAtMs =
|
|
1613
1643
|
previous?.phase === phase
|
|
1614
1644
|
? previous.atMs
|
|
@@ -1616,10 +1646,12 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1616
1646
|
? details.atMs
|
|
1617
1647
|
: Date.now()
|
|
1618
1648
|
const record = {
|
|
1649
|
+
delegationId,
|
|
1619
1650
|
agentThreadId,
|
|
1620
1651
|
agentPath,
|
|
1621
1652
|
phase,
|
|
1622
1653
|
agent: metadata?.agent || previous?.agent || subagentFallbackName(agentPath),
|
|
1654
|
+
agentExplicit: metadata?.agentExplicit === true || previous?.agentExplicit === true,
|
|
1623
1655
|
description:
|
|
1624
1656
|
metadata?.description || previous?.description || subagentFallbackName(agentPath),
|
|
1625
1657
|
prompt: metadata?.prompt || previous?.prompt || "",
|
|
@@ -1638,15 +1670,30 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1638
1670
|
return record
|
|
1639
1671
|
}
|
|
1640
1672
|
|
|
1641
|
-
_observeSubagentItem(
|
|
1673
|
+
_observeSubagentItem(
|
|
1674
|
+
session,
|
|
1675
|
+
item,
|
|
1676
|
+
atMs = Date.now(),
|
|
1677
|
+
{ emit = true, sourceThreadId = session?.threadId } = {},
|
|
1678
|
+
) {
|
|
1642
1679
|
if (!item || typeof item !== "object") return
|
|
1680
|
+
// root thread の collaboration item だけが root subagent 台帳の正本。子 thread 内の
|
|
1681
|
+
// spawn/subAgentActivity(孫)を root の lifecycle/snapshot へ昇格しない。
|
|
1682
|
+
if (
|
|
1683
|
+
typeof sourceThreadId === "string" &&
|
|
1684
|
+
sourceThreadId &&
|
|
1685
|
+
sourceThreadId !== session?.threadId
|
|
1686
|
+
) return
|
|
1643
1687
|
if (item.type === "dynamicToolCall") {
|
|
1644
1688
|
const namespace = item.namespace
|
|
1645
1689
|
if (namespace !== "collaboration" || item.tool !== "spawn_agent" || !item.id) return
|
|
1646
1690
|
const args = parseDynamicToolArguments(item.arguments)
|
|
1647
1691
|
if (!args) return
|
|
1692
|
+
const agent = typeof args.agent_type === "string" ? args.agent_type.trim() : ""
|
|
1693
|
+
const agentExplicit = agent.length > 0
|
|
1648
1694
|
const metadata = {
|
|
1649
|
-
agent:
|
|
1695
|
+
agent: agentExplicit ? agent : null,
|
|
1696
|
+
agentExplicit,
|
|
1650
1697
|
description: typeof args.task_name === "string" && args.task_name ? args.task_name : null,
|
|
1651
1698
|
prompt: typeof args.message === "string" ? args.message : null,
|
|
1652
1699
|
background: args.background === true,
|
|
@@ -1674,6 +1721,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1674
1721
|
agentThreadId,
|
|
1675
1722
|
phase,
|
|
1676
1723
|
{
|
|
1724
|
+
delegationId: item.id,
|
|
1677
1725
|
agentPath: item.agentPath,
|
|
1678
1726
|
metadata,
|
|
1679
1727
|
atMs,
|
|
@@ -1736,10 +1784,11 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1736
1784
|
const startedHistoryIndex = startedIndexByThread.get(record.agentThreadId)
|
|
1737
1785
|
if (startedHistoryIndex === undefined) continue
|
|
1738
1786
|
snapshots.push({
|
|
1739
|
-
delegationId: record.
|
|
1787
|
+
delegationId: record.delegationId,
|
|
1740
1788
|
agentThreadId: record.agentThreadId,
|
|
1741
1789
|
agentPath: record.agentPath,
|
|
1742
1790
|
agent: record.agent,
|
|
1791
|
+
agentExplicit: record.agentExplicit === true,
|
|
1743
1792
|
description: record.description,
|
|
1744
1793
|
prompt: record.prompt,
|
|
1745
1794
|
background: record.background === true,
|
|
@@ -1773,6 +1822,10 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1773
1822
|
// RPC 待ち中に live lifecycle が進んだ場合、stale read で上書き・削除しない。
|
|
1774
1823
|
if (session.subagents.get(record.agentThreadId) !== record) continue
|
|
1775
1824
|
const thread = result?.thread
|
|
1825
|
+
const explicitRole = explicitSubagentRoleFromThread(thread, session.threadId)
|
|
1826
|
+
const metadata = explicitRole
|
|
1827
|
+
? { agent: explicitRole, agentExplicit: true }
|
|
1828
|
+
: null
|
|
1776
1829
|
const turns = Array.isArray(thread?.turns) ? thread.turns : []
|
|
1777
1830
|
const lastTurn = turns.at(-1)
|
|
1778
1831
|
const phase = terminalSubagentPhase(lastTurn?.status)
|
|
@@ -1787,7 +1840,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1787
1840
|
session,
|
|
1788
1841
|
record.agentThreadId,
|
|
1789
1842
|
phase,
|
|
1790
|
-
{ agentPath: record.agentPath, atMs: lastTurnAtMs },
|
|
1843
|
+
{ agentPath: record.agentPath, metadata, atMs: lastTurnAtMs },
|
|
1791
1844
|
{ emit: false },
|
|
1792
1845
|
)
|
|
1793
1846
|
} else if (
|
|
@@ -1800,6 +1853,7 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1800
1853
|
"started",
|
|
1801
1854
|
{
|
|
1802
1855
|
agentPath: record.agentPath,
|
|
1856
|
+
metadata,
|
|
1803
1857
|
atMs: record.startedAtMs,
|
|
1804
1858
|
},
|
|
1805
1859
|
{ emit: false },
|
|
@@ -1880,25 +1934,46 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1880
1934
|
// ※ hub-agent 再起動 (cold resume) では historyItems が揮発するため青バブルへ戻る (claude の
|
|
1881
1935
|
// jsonl at-rest と同じ許容範囲。source 永続マップは TTL/ID 再利用の複雑さに見合わないため不採用)。
|
|
1882
1936
|
const eventThreadId = msg?.params?.threadId
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
//
|
|
1937
|
+
const taggedParams = this._tagInjectSourceOnItem(session, msg)
|
|
1938
|
+
let effectiveParams = taggedParams || msg.params
|
|
1939
|
+
const childRecord =
|
|
1940
|
+
typeof eventThreadId === "string" ? session.subagents.get(eventThreadId) : null
|
|
1941
|
+
const childTextDelta =
|
|
1942
|
+
msg.method === "item/agentMessage/delta" ||
|
|
1943
|
+
msg.method === "item/reasoning/summaryTextDelta" ||
|
|
1944
|
+
msg.method === "item/reasoning/textDelta"
|
|
1945
|
+
if (childRecord && childTextDelta) {
|
|
1946
|
+
// 子の message/reasoning 途中 delta を root liveText へ混ぜない。前景・背景とも
|
|
1947
|
+
// attribution 付き item/completed が表示の正本で、root/unknown delta は従来どおり
|
|
1948
|
+
// fail-open で通す。
|
|
1893
1949
|
return
|
|
1894
1950
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1951
|
+
if (
|
|
1952
|
+
childRecord &&
|
|
1953
|
+
(msg.method === "item/started" || msg.method === "item/completed") &&
|
|
1954
|
+
effectiveParams?.item &&
|
|
1955
|
+
typeof effectiveParams.item === "object"
|
|
1956
|
+
) {
|
|
1957
|
+
// app-server 所有の item は変更せず、既知の子 thread だけを attribution 付きで複製する。
|
|
1958
|
+
// unknown/root を推測で子扱いしない。delegationId は child thread ID ではなく、spawn
|
|
1959
|
+
// lifecycle が保持する安定 ID を使う。
|
|
1960
|
+
const attributedItem = {
|
|
1961
|
+
...effectiveParams.item,
|
|
1962
|
+
_hubSubagent: {
|
|
1963
|
+
delegationId: childRecord.delegationId,
|
|
1964
|
+
threadId: eventThreadId,
|
|
1965
|
+
runtime: "codex",
|
|
1966
|
+
background: childRecord.background === true,
|
|
1967
|
+
},
|
|
1968
|
+
}
|
|
1969
|
+
effectiveParams = { ...effectiveParams, item: attributedItem }
|
|
1970
|
+
}
|
|
1897
1971
|
if (msg.method === "item/started" || msg.method === "item/completed") {
|
|
1898
1972
|
this._observeSubagentItem(
|
|
1899
1973
|
session,
|
|
1900
1974
|
effectiveParams?.item,
|
|
1901
1975
|
effectiveParams?.completedAtMs || Date.now(),
|
|
1976
|
+
{ sourceThreadId: eventThreadId },
|
|
1902
1977
|
)
|
|
1903
1978
|
}
|
|
1904
1979
|
const childTurnNotification =
|
|
@@ -1968,8 +2043,8 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
1968
2043
|
void this._pumpInputs(session)
|
|
1969
2044
|
break
|
|
1970
2045
|
case "item/completed":
|
|
1971
|
-
//
|
|
1972
|
-
//
|
|
2046
|
+
// 完了した ThreadItem を高忠実度履歴に追記する(cwd reattach で返す用)。
|
|
2047
|
+
// attribution/source 済み item (effectiveParams.item) を積み、emit と同じ clone を使う。
|
|
1973
2048
|
this._appendHistoryItem(session, effectiveParams?.item, effectiveParams?.completedAtMs)
|
|
1974
2049
|
break
|
|
1975
2050
|
default:
|
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,
|