agent-dag 1.14.2 → 1.15.1
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/dist/web/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>agent-dag</title>
|
|
7
7
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='84' font-size='84'%3E%E2%97%89%3C/text%3E%3C/svg%3E" />
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Bq4SSF-l.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-Bwj0xSAo.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/package.json
CHANGED
package/src/server/index.mjs
CHANGED
|
@@ -73,32 +73,59 @@ async function maybeRotatePersistFile() {
|
|
|
73
73
|
// payloads for that session before broadcasting, (b) emit a synthetic
|
|
74
74
|
// `ModelObserved` event so the client backfills agents created before
|
|
75
75
|
// the model was resolved.
|
|
76
|
-
const modelBySession = new Map(); // sessionId ->
|
|
76
|
+
const modelBySession = new Map(); // sessionId -> { rootModel, subsSig }
|
|
77
77
|
const pendingTranscriptReads = new Set(); // sessionId currently being read
|
|
78
|
+
const modelLastReadAt = new Map(); // sessionId -> ms timestamp (re-read throttle)
|
|
79
|
+
const MODEL_READ_THROTTLE_MS = 2500;
|
|
78
80
|
|
|
79
81
|
async function readModelFromTranscript(path) {
|
|
82
|
+
// Returns { rootModel, subagentModels } — root is the most recent
|
|
83
|
+
// non-sidechain assistant model, subagents are attributed by the Task
|
|
84
|
+
// tool_use id that owns them. CC transcripts mark subagent messages
|
|
85
|
+
// with `isSidechain:true` and (for current schemas) a `parentToolUseID`
|
|
86
|
+
// or `parent_tool_use_id` referencing the Task invocation.
|
|
80
87
|
try {
|
|
81
88
|
const s = await stat(path);
|
|
82
89
|
if (s.size === 0) return null;
|
|
83
|
-
// Read up to last 128 KB — plenty for the most-recent model
|
|
84
|
-
// declaration. Reading from the tail handles sessions that switched
|
|
85
|
-
// model mid-conversation (we want the current one).
|
|
86
|
-
const TAIL = 128 * 1024;
|
|
87
|
-
const start = Math.max(0, s.size - TAIL);
|
|
88
90
|
const fh = await open(path, "r");
|
|
91
|
+
let text;
|
|
89
92
|
try {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const text = buf.toString("utf8");
|
|
94
|
-
// Scan all matches and return the LAST one — most recent model used.
|
|
95
|
-
const re = /"model"\s*:\s*"(claude[-_][^"]+)"/gi;
|
|
96
|
-
let last = null;
|
|
97
|
-
for (const m of text.matchAll(re)) last = m[1];
|
|
98
|
-
return last;
|
|
93
|
+
const buf = Buffer.alloc(s.size);
|
|
94
|
+
await fh.read(buf, 0, s.size, 0);
|
|
95
|
+
text = buf.toString("utf8");
|
|
99
96
|
} finally {
|
|
100
97
|
await fh.close();
|
|
101
98
|
}
|
|
99
|
+
let rootModel = null;
|
|
100
|
+
const subagentModels = {};
|
|
101
|
+
let anyModelSeen = null; // regex-style fallback — the last claude-* model
|
|
102
|
+
// we encountered, regardless of sidechain flag.
|
|
103
|
+
// Keeps the old behavior alive when the JSONL
|
|
104
|
+
// schema doesn't expose `isSidechain`.
|
|
105
|
+
for (const line of text.split("\n")) {
|
|
106
|
+
if (!line) continue;
|
|
107
|
+
let obj;
|
|
108
|
+
try { obj = JSON.parse(line); } catch { continue; }
|
|
109
|
+
const msg = obj && obj.message;
|
|
110
|
+
const model = (msg && typeof msg.model === "string" && /^claude[-_]/i.test(msg.model)) ? msg.model
|
|
111
|
+
: (typeof obj.model === "string" && /^claude[-_]/i.test(obj.model)) ? obj.model
|
|
112
|
+
: null;
|
|
113
|
+
if (!model) continue;
|
|
114
|
+
anyModelSeen = model;
|
|
115
|
+
const isSide = obj.isSidechain === true || obj.is_sidechain === true;
|
|
116
|
+
const ptid = obj.parentToolUseID || obj.parent_tool_use_id || obj.parentToolUseId || null;
|
|
117
|
+
if (isSide && ptid) {
|
|
118
|
+
subagentModels[ptid] = model;
|
|
119
|
+
} else if (!isSide) {
|
|
120
|
+
rootModel = model;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Fallback: if no `isSidechain:false` entry was found (older schema, or
|
|
124
|
+
// a transcript that only contains sidechain blocks), use the last-seen
|
|
125
|
+
// model so the root card still shows SOMETHING instead of going blank.
|
|
126
|
+
if (!rootModel) rootModel = anyModelSeen;
|
|
127
|
+
if (!rootModel && Object.keys(subagentModels).length === 0) return null;
|
|
128
|
+
return { rootModel, subagentModels };
|
|
102
129
|
} catch {
|
|
103
130
|
return null;
|
|
104
131
|
}
|
|
@@ -109,16 +136,29 @@ function maybeResolveModel(payload) {
|
|
|
109
136
|
const sid = payload.session_id;
|
|
110
137
|
const tp = payload.transcript_path;
|
|
111
138
|
if (!sid || !tp) return;
|
|
112
|
-
|
|
139
|
+
// Re-read on every event for this session — the cache was preventing us
|
|
140
|
+
// from picking up subagent models that arrive after the root is known.
|
|
141
|
+
// Throttle so we don't thrash the filesystem.
|
|
113
142
|
if (pendingTranscriptReads.has(sid)) return;
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
const last = modelLastReadAt.get(sid) ?? 0;
|
|
145
|
+
if (now - last < MODEL_READ_THROTTLE_MS) return;
|
|
146
|
+
modelLastReadAt.set(sid, now);
|
|
114
147
|
pendingTranscriptReads.add(sid);
|
|
115
148
|
readModelFromTranscript(tp)
|
|
116
|
-
.then(
|
|
117
|
-
if (!
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
149
|
+
.then(result => {
|
|
150
|
+
if (!result) return;
|
|
151
|
+
const { rootModel, subagentModels } = result;
|
|
152
|
+
const prev = modelBySession.get(sid);
|
|
153
|
+
const subsSig = JSON.stringify(subagentModels);
|
|
154
|
+
if (prev && prev.rootModel === rootModel && prev.subsSig === subsSig) return;
|
|
155
|
+
modelBySession.set(sid, { rootModel, subsSig });
|
|
156
|
+
pushEvent({
|
|
157
|
+
hook_event_name: "ModelObserved",
|
|
158
|
+
session_id: sid,
|
|
159
|
+
model: rootModel,
|
|
160
|
+
subagentModels,
|
|
161
|
+
}, "internal");
|
|
122
162
|
})
|
|
123
163
|
.catch(() => {})
|
|
124
164
|
.finally(() => pendingTranscriptReads.delete(sid));
|