@stage-labs/metro 0.1.0-beta.273 → 0.1.0-beta.274
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": "metro",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.274",
|
|
4
4
|
"description": "Metro for Claude Code, installed by the metro daemon on this machine: every connector the agent holds as its own MCP server relayed through the daemon, the orchestrator-only guard on the main thread, and the standing rules loaded at each session start. No vendor credential ever sits in a config file.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bonustrack",
|
package/runtime/runtime.json
CHANGED
|
@@ -2,6 +2,7 @@ import { query, type CanUseTool, type Options, type Query, type SDKMessage, type
|
|
|
2
2
|
import { errMsg, log } from '@metro-labs/core/log';
|
|
3
3
|
import { isRecord } from '@metro-labs/core/is-record';
|
|
4
4
|
import type { Activity } from './activity.js';
|
|
5
|
+
import { ActivityTasks } from './activity-tasks.js';
|
|
5
6
|
import { RunnerCalls } from './calls.js';
|
|
6
7
|
import { channelText, type ChannelEvent } from './channel-text.js';
|
|
7
8
|
import type { RunnerConfig } from './config.js';
|
|
@@ -14,6 +15,7 @@ import { METRO_SERVER, type MetroTools } from './tool-proxy.js';
|
|
|
14
15
|
|
|
15
16
|
const STARTUP_WAIT = 'CLAUDE_CODE_MCP_STARTUP_WAIT_MS';
|
|
16
17
|
export const COMPACT_AT = 120_000;
|
|
18
|
+
export const COMPACT_IDLE_MS = 60_000;
|
|
17
19
|
|
|
18
20
|
const MODEL_PINS: ReadonlySet<string> = new Set(['ANTHROPIC_MODEL', 'CLAUDE_CODE_SUBAGENT_MODEL']);
|
|
19
21
|
|
|
@@ -85,6 +87,8 @@ export class Runner {
|
|
|
85
87
|
readonly inbox: Inbox;
|
|
86
88
|
readonly calls: RunnerCalls;
|
|
87
89
|
private readonly watch: SessionWatch;
|
|
90
|
+
private readonly tasks = new ActivityTasks(() => undefined);
|
|
91
|
+
private compactTimer: ReturnType<typeof setTimeout> | null = null;
|
|
88
92
|
private session: Query | null = null;
|
|
89
93
|
private sessionId: string | null = null;
|
|
90
94
|
private compactAsked: Uuid | null = null;
|
|
@@ -101,7 +105,7 @@ export class Runner {
|
|
|
101
105
|
this.inbox = new Inbox((unanswered) => { this.keep(unanswered); }, {
|
|
102
106
|
ready: () => this.watch.admitting,
|
|
103
107
|
dispatch: (message) => { this.watch.dispatched(message.uuid); },
|
|
104
|
-
input: (input) => { parts.activity?.input(input); },
|
|
108
|
+
input: (input) => { this.cancelCompact(); parts.activity?.input(input); },
|
|
105
109
|
queue: (count, oldest) => { parts.activity?.pending(count, oldest); },
|
|
106
110
|
});
|
|
107
111
|
this.calls = new RunnerCalls(this.inbox, parts.activity);
|
|
@@ -133,15 +137,19 @@ export class Runner {
|
|
|
133
137
|
if (this.session === null) throw new Error('the runner was not started');
|
|
134
138
|
try {
|
|
135
139
|
for await (const message of this.session) {
|
|
140
|
+
this.cancelCompact();
|
|
141
|
+
if (message.type === 'system') this.tasks.observe(message);
|
|
136
142
|
const m: Record<string, unknown> = { ...message };
|
|
137
143
|
this.trackInput(m);
|
|
138
144
|
this.watch.observe(m);
|
|
139
145
|
this.note(m);
|
|
140
146
|
this.parts.activity?.observe(message);
|
|
141
147
|
this.inbox.notify();
|
|
148
|
+
this.maybeCompact();
|
|
142
149
|
observe?.(message);
|
|
143
150
|
}
|
|
144
151
|
} finally {
|
|
152
|
+
this.cancelCompact();
|
|
145
153
|
this.ended = true;
|
|
146
154
|
}
|
|
147
155
|
}
|
|
@@ -163,10 +171,12 @@ export class Runner {
|
|
|
163
171
|
}
|
|
164
172
|
|
|
165
173
|
close(cancelActive = true): void {
|
|
174
|
+
this.cancelCompact();
|
|
166
175
|
this.calls.close();
|
|
167
176
|
try {
|
|
168
177
|
if (cancelActive && !this.ended) this.inbox.finished();
|
|
169
178
|
} finally {
|
|
179
|
+
this.ended = true;
|
|
170
180
|
this.inbox.close();
|
|
171
181
|
this.session?.close();
|
|
172
182
|
}
|
|
@@ -218,7 +228,6 @@ export class Runner {
|
|
|
218
228
|
if (m.type === 'result') this.turnDone(m);
|
|
219
229
|
if (m.type !== 'system') return;
|
|
220
230
|
if (m.subtype === 'init') this.started(m);
|
|
221
|
-
else if (m.subtype === 'session_state_changed' && m.state === 'idle') this.maybeCompact();
|
|
222
231
|
else if (m.subtype === 'compact_boundary') {
|
|
223
232
|
this.compacted = true;
|
|
224
233
|
this.compactFloor = null;
|
|
@@ -239,16 +248,33 @@ export class Runner {
|
|
|
239
248
|
this.compactFloor = context;
|
|
240
249
|
this.compacted = false;
|
|
241
250
|
}
|
|
242
|
-
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private compactReady(resuming = false): boolean {
|
|
254
|
+
const busy = this.ended || this.compactAsked !== null || !this.watch.safe || !this.watch.admitting || (!resuming && (this.calls.live || this.inbox.pending > 0 || this.tasks.running > 0));
|
|
255
|
+
return compactDue({ context: this.watch.context, floor: this.compactFloor, limit: this.parts.compactAt ?? COMPACT_AT, busy });
|
|
243
256
|
}
|
|
244
257
|
|
|
245
258
|
private maybeCompact(resuming = false): void {
|
|
246
|
-
|
|
247
|
-
if (
|
|
259
|
+
if (!this.compactReady(resuming)) return;
|
|
260
|
+
if (resuming) { this.askCompact(true); return; }
|
|
261
|
+
this.compactTimer = setTimeout(() => {
|
|
262
|
+
this.compactTimer = null;
|
|
263
|
+
if (this.compactReady()) this.askCompact(false);
|
|
264
|
+
}, COMPACT_IDLE_MS);
|
|
265
|
+
this.compactTimer.unref();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private askCompact(resuming: boolean): void {
|
|
248
269
|
this.compactAsked = this.inbox.compact();
|
|
249
270
|
log.info({ context: this.watch.context, resuming }, 'sdk-runner: compacting at a safe idle boundary');
|
|
250
271
|
}
|
|
251
272
|
|
|
273
|
+
private cancelCompact(): void {
|
|
274
|
+
if (this.compactTimer !== null) clearTimeout(this.compactTimer);
|
|
275
|
+
this.compactTimer = null;
|
|
276
|
+
}
|
|
277
|
+
|
|
252
278
|
private started(m: Record<string, unknown>): void {
|
|
253
279
|
const id = typeof m.session_id === 'string' ? m.session_id : null;
|
|
254
280
|
if (id === null || id === this.sessionId) return;
|