@compilr-dev/sdk 0.23.1 → 0.24.0
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/team/team-agent.d.ts +56 -1
- package/dist/team/team-agent.js +79 -2
- package/dist/team/team.d.ts +3 -1
- package/dist/team/types.d.ts +12 -0
- package/package.json +1 -1
|
@@ -68,6 +68,8 @@ export interface TeamAgentUpdate {
|
|
|
68
68
|
toolProfile?: TeamAgentConfig['toolProfile'];
|
|
69
69
|
toolFilter?: string[];
|
|
70
70
|
enabledSkills?: string[];
|
|
71
|
+
/** Which MCP servers this agent may reach. See `TeamAgent.mcpServers`. */
|
|
72
|
+
mcpServers?: string[];
|
|
71
73
|
}
|
|
72
74
|
/** What an edit did, and whether anyone needs telling. */
|
|
73
75
|
export interface AgentUpdateResult {
|
|
@@ -185,6 +187,15 @@ export declare class TeamAgent {
|
|
|
185
187
|
* from the profile name alone.
|
|
186
188
|
*/
|
|
187
189
|
customGroups?: string[];
|
|
190
|
+
/**
|
|
191
|
+
* MCP servers this agent may reach. `undefined` = a pre-grants agent, which reaches all;
|
|
192
|
+
* `[]` = granted nothing. See `TeamAgentConfig.mcpServers`.
|
|
193
|
+
*
|
|
194
|
+
* The SDK holds the grant; the HOST enforces it, because the host owns the MCP connection and
|
|
195
|
+
* decides which tools an agent is built with. `grantsMcpServer()` is the shared predicate so
|
|
196
|
+
* the two cannot disagree about what `undefined` means.
|
|
197
|
+
*/
|
|
198
|
+
mcpServers?: string[];
|
|
188
199
|
/**
|
|
189
200
|
* Enabled skills (empty = all skills)
|
|
190
201
|
*/
|
|
@@ -272,6 +283,37 @@ export declare class TeamAgent {
|
|
|
272
283
|
* `thought_signature`) that do not transfer across a rebuild.
|
|
273
284
|
*/
|
|
274
285
|
private stashForRebuild;
|
|
286
|
+
/**
|
|
287
|
+
* A turn is in flight: the host has called `run()`/`stream()` and is awaiting it.
|
|
288
|
+
*
|
|
289
|
+
* ⚠️ HOSTS MUST BRACKET EVERY RUN, IN A `finally`. `TeamAgent` does not own the run call —
|
|
290
|
+
* the host holds the unwrapped `Agent` — so it cannot observe this for itself.
|
|
291
|
+
*/
|
|
292
|
+
private turnStartedAt;
|
|
293
|
+
/**
|
|
294
|
+
* How long a turn may be in flight before this stops believing the host.
|
|
295
|
+
*
|
|
296
|
+
* ⚠️ A FLAG THAT ONLY EVER GETS SET IS WORSE THAN THE BUG IT GUARDS. A host that throws past
|
|
297
|
+
* its `endTurn()` would block every edit to that agent for the life of the process, with no
|
|
298
|
+
* way back short of a restart. Going stale bounds that to one window; the edit that slips
|
|
299
|
+
* through after it is the same edit that would have been allowed before this guard existed.
|
|
300
|
+
*/
|
|
301
|
+
private static readonly TURN_STALE_AFTER_MS;
|
|
302
|
+
/**
|
|
303
|
+
* Whether this agent may reach an MCP server.
|
|
304
|
+
*
|
|
305
|
+
* ⚠️ THE ONE PLACE `undefined` IS INTERPRETED. Hosts filter the tools they build an agent
|
|
306
|
+
* with, so each one would otherwise re-decide what "never asked" means — and a host that
|
|
307
|
+
* read it as "deny" would silently strip reach from every agent created before grants
|
|
308
|
+
* existed, which is the migration we explicitly chose not to do.
|
|
309
|
+
*/
|
|
310
|
+
grantsMcpServer(server: string): boolean;
|
|
311
|
+
/** Mark the start of a turn. Pair with `endTurn()` in a `finally`. */
|
|
312
|
+
beginTurn(): void;
|
|
313
|
+
/** Mark the end of a turn, however it ended — completed, aborted or thrown. */
|
|
314
|
+
endTurn(): void;
|
|
315
|
+
/** Whether a turn is in flight and recent enough to still be believed. */
|
|
316
|
+
get isTurnInFlight(): boolean;
|
|
275
317
|
/**
|
|
276
318
|
* Apply an edit to this agent in place.
|
|
277
319
|
*
|
|
@@ -312,7 +354,20 @@ export declare class TeamAgent {
|
|
|
312
354
|
* @param agentFactory Factory function to create the agent
|
|
313
355
|
* @param sharedContext Optional shared context to inject into system prompt
|
|
314
356
|
*/
|
|
315
|
-
initialize(agentFactory: (config: Partial<AgentConfig>, systemPromptAddition?: string, useMinimalSystemPrompt?: boolean, noTools?: boolean, toolFilter?: string[], modelTier?: ModelTier
|
|
357
|
+
initialize(agentFactory: (config: Partial<AgentConfig>, systemPromptAddition?: string, useMinimalSystemPrompt?: boolean, noTools?: boolean, toolFilter?: string[], modelTier?: ModelTier,
|
|
358
|
+
/**
|
|
359
|
+
* Which agent is being built.
|
|
360
|
+
*
|
|
361
|
+
* ⚠️ ADDED BECAUSE THE FACTORY COULD NOT TELL ITS AGENTS APART. Per-agent MCP grants are
|
|
362
|
+
* enforced by the HOST — it owns the connection and decides which tools an agent is
|
|
363
|
+
* constructed with — and it could not, because every parameter here described the
|
|
364
|
+
* configuration and none identified the agent. Desktop's episode recorder had already
|
|
365
|
+
* worked around the same gap by recording every team agent under the literal id
|
|
366
|
+
* 'team-agent'.
|
|
367
|
+
*
|
|
368
|
+
* Optional, so a host that ignores it behaves exactly as before.
|
|
369
|
+
*/
|
|
370
|
+
teamAgent?: TeamAgent) => Promise<Agent>, sharedContext?: SharedContextManager): Promise<void>;
|
|
316
371
|
/**
|
|
317
372
|
* Set the agent instance directly
|
|
318
373
|
* Used for the default agent when an agent is created before the team
|
package/dist/team/team-agent.js
CHANGED
|
@@ -203,6 +203,15 @@ export class TeamAgent {
|
|
|
203
203
|
* from the profile name alone.
|
|
204
204
|
*/
|
|
205
205
|
customGroups;
|
|
206
|
+
/**
|
|
207
|
+
* MCP servers this agent may reach. `undefined` = a pre-grants agent, which reaches all;
|
|
208
|
+
* `[]` = granted nothing. See `TeamAgentConfig.mcpServers`.
|
|
209
|
+
*
|
|
210
|
+
* The SDK holds the grant; the HOST enforces it, because the host owns the MCP connection and
|
|
211
|
+
* decides which tools an agent is built with. `grantsMcpServer()` is the shared predicate so
|
|
212
|
+
* the two cannot disagree about what `undefined` means.
|
|
213
|
+
*/
|
|
214
|
+
mcpServers;
|
|
206
215
|
/**
|
|
207
216
|
* Enabled skills (empty = all skills)
|
|
208
217
|
*/
|
|
@@ -261,6 +270,7 @@ export class TeamAgent {
|
|
|
261
270
|
this.toolFilter = config.toolFilter;
|
|
262
271
|
this.toolProfile = config.toolProfile;
|
|
263
272
|
this.customGroups = config.customGroups;
|
|
273
|
+
this.mcpServers = config.mcpServers;
|
|
264
274
|
this.enabledSkills = config.enabledSkills;
|
|
265
275
|
this.personality = config.personality;
|
|
266
276
|
this.autoApproveHandoff = config.autoApproveHandoff;
|
|
@@ -337,6 +347,47 @@ export class TeamAgent {
|
|
|
337
347
|
this.storedState = state;
|
|
338
348
|
this._agent = null;
|
|
339
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* A turn is in flight: the host has called `run()`/`stream()` and is awaiting it.
|
|
352
|
+
*
|
|
353
|
+
* ⚠️ HOSTS MUST BRACKET EVERY RUN, IN A `finally`. `TeamAgent` does not own the run call —
|
|
354
|
+
* the host holds the unwrapped `Agent` — so it cannot observe this for itself.
|
|
355
|
+
*/
|
|
356
|
+
turnStartedAt = null;
|
|
357
|
+
/**
|
|
358
|
+
* How long a turn may be in flight before this stops believing the host.
|
|
359
|
+
*
|
|
360
|
+
* ⚠️ A FLAG THAT ONLY EVER GETS SET IS WORSE THAN THE BUG IT GUARDS. A host that throws past
|
|
361
|
+
* its `endTurn()` would block every edit to that agent for the life of the process, with no
|
|
362
|
+
* way back short of a restart. Going stale bounds that to one window; the edit that slips
|
|
363
|
+
* through after it is the same edit that would have been allowed before this guard existed.
|
|
364
|
+
*/
|
|
365
|
+
static TURN_STALE_AFTER_MS = 10 * 60 * 1000;
|
|
366
|
+
/**
|
|
367
|
+
* Whether this agent may reach an MCP server.
|
|
368
|
+
*
|
|
369
|
+
* ⚠️ THE ONE PLACE `undefined` IS INTERPRETED. Hosts filter the tools they build an agent
|
|
370
|
+
* with, so each one would otherwise re-decide what "never asked" means — and a host that
|
|
371
|
+
* read it as "deny" would silently strip reach from every agent created before grants
|
|
372
|
+
* existed, which is the migration we explicitly chose not to do.
|
|
373
|
+
*/
|
|
374
|
+
grantsMcpServer(server) {
|
|
375
|
+
return this.mcpServers === undefined || this.mcpServers.includes(server);
|
|
376
|
+
}
|
|
377
|
+
/** Mark the start of a turn. Pair with `endTurn()` in a `finally`. */
|
|
378
|
+
beginTurn() {
|
|
379
|
+
this.turnStartedAt = Date.now();
|
|
380
|
+
}
|
|
381
|
+
/** Mark the end of a turn, however it ended — completed, aborted or thrown. */
|
|
382
|
+
endTurn() {
|
|
383
|
+
this.turnStartedAt = null;
|
|
384
|
+
}
|
|
385
|
+
/** Whether a turn is in flight and recent enough to still be believed. */
|
|
386
|
+
get isTurnInFlight() {
|
|
387
|
+
if (this.turnStartedAt === null)
|
|
388
|
+
return false;
|
|
389
|
+
return Date.now() - this.turnStartedAt < TeamAgent.TURN_STALE_AFTER_MS;
|
|
390
|
+
}
|
|
340
391
|
/**
|
|
341
392
|
* Apply an edit to this agent in place.
|
|
342
393
|
*
|
|
@@ -354,12 +405,33 @@ export class TeamAgent {
|
|
|
354
405
|
(patch.personality !== undefined && patch.personality !== this.personality) ||
|
|
355
406
|
(patch.toolProfile !== undefined && patch.toolProfile !== this.toolProfile) ||
|
|
356
407
|
(patch.toolFilter !== undefined && !sameStrings(patch.toolFilter, this.toolFilter)) ||
|
|
357
|
-
(patch.enabledSkills !== undefined &&
|
|
408
|
+
(patch.enabledSkills !== undefined &&
|
|
409
|
+
!sameStrings(patch.enabledSkills, this.enabledSkills)) ||
|
|
410
|
+
(patch.mcpServers !== undefined && !sameStrings(patch.mcpServers, this.mcpServers));
|
|
358
411
|
/*
|
|
359
412
|
Delegated: `setModelTier` no-ops when the tier is unchanged and stashes for rebuild
|
|
360
413
|
when it is not. Its `rebuilt` contribution is folded in below.
|
|
361
414
|
*/
|
|
362
415
|
const tierChanged = patch.modelTier !== undefined && patch.modelTier !== this._modelTier;
|
|
416
|
+
/*
|
|
417
|
+
⚠️ REFUSED MID-TURN, BEFORE ANYTHING IS MUTATED. A rebuild snapshots the history with
|
|
418
|
+
`serialize()` and drops the instance — but the in-flight `run()` holds its own reference
|
|
419
|
+
and finishes anyway, appending to an agent nobody will read again. Measured: the user
|
|
420
|
+
watches a reply stream in, and the rebuilt agent is handed the snapshot from BEFORE it,
|
|
421
|
+
with no memory of having said it.
|
|
422
|
+
|
|
423
|
+
Not a broken conversation — `setHistory` repairs tool pairing, so nothing 400s. It is
|
|
424
|
+
worse than that: the agent silently forgets a turn the user just watched, and the next
|
|
425
|
+
thing it says is built on a history the user can see is wrong.
|
|
426
|
+
|
|
427
|
+
Only rebuilding edits are refused. A rename or a new face is read from the roster at
|
|
428
|
+
render time and cannot tear anything, so those still apply mid-turn.
|
|
429
|
+
*/
|
|
430
|
+
if ((needsRebuild || tierChanged) && this.isTurnInFlight) {
|
|
431
|
+
throw new Error(`Agent '${this.id}' is mid-reply. Changing its prompt, model, tools or skills rebuilds ` +
|
|
432
|
+
`it and would drop the turn it is in the middle of — wait for the reply to finish, ` +
|
|
433
|
+
`or stop it, then save.`);
|
|
434
|
+
}
|
|
363
435
|
if (patch.modelTier !== undefined)
|
|
364
436
|
this.setModelTier(patch.modelTier);
|
|
365
437
|
if (patch.displayName !== undefined)
|
|
@@ -372,6 +444,8 @@ export class TeamAgent {
|
|
|
372
444
|
this.systemPromptAddition = patch.systemPromptAddition;
|
|
373
445
|
if (patch.personality !== undefined)
|
|
374
446
|
this.personality = patch.personality;
|
|
447
|
+
if (patch.mcpServers !== undefined)
|
|
448
|
+
this.mcpServers = patch.mcpServers;
|
|
375
449
|
/*
|
|
376
450
|
⚠️ THE PROFILE IS A LABEL; `toolFilter` IS THE ENFORCEMENT. `initialize()` passes
|
|
377
451
|
`toolFilter` to the factory and never looks at `toolProfile`, so setting the profile
|
|
@@ -501,7 +575,8 @@ export class TeamAgent {
|
|
|
501
575
|
}
|
|
502
576
|
}
|
|
503
577
|
this._agent = await agentFactory(this.agentConfig, finalSystemPromptAddition || undefined, this.useMinimalSystemPrompt, this.noTools, this.toolFilter, // Pass tool filter to factory
|
|
504
|
-
this._modelTier // Pass model tier to factory
|
|
578
|
+
this._modelTier, // Pass model tier to factory
|
|
579
|
+
this // So the host can honour this agent's MCP grants and name it in logs
|
|
505
580
|
);
|
|
506
581
|
// Set the team roster as a pin (dynamically re-injected on every LLM call).
|
|
507
582
|
// This ensures roster updates (agent added/removed) are always visible.
|
|
@@ -682,6 +757,7 @@ export class TeamAgent {
|
|
|
682
757
|
toolFilter: this.toolFilter,
|
|
683
758
|
toolProfile: this.toolProfile,
|
|
684
759
|
customGroups: this.customGroups,
|
|
760
|
+
mcpServers: this.mcpServers,
|
|
685
761
|
enabledSkills: this.enabledSkills,
|
|
686
762
|
modelTier: this.modelTier,
|
|
687
763
|
autoApproveHandoff: this.autoApproveHandoff,
|
|
@@ -738,6 +814,7 @@ export class TeamAgent {
|
|
|
738
814
|
toolFilter,
|
|
739
815
|
toolProfile: data.toolProfile,
|
|
740
816
|
customGroups: data.customGroups,
|
|
817
|
+
mcpServers: data.mcpServers,
|
|
741
818
|
enabledSkills: data.enabledSkills,
|
|
742
819
|
modelTier: data.modelTier,
|
|
743
820
|
autoApproveHandoff: data.autoApproveHandoff,
|
package/dist/team/team.d.ts
CHANGED
|
@@ -34,7 +34,9 @@ export interface AgentTeamConfig {
|
|
|
34
34
|
* @param toolFilter - Limit which tools this agent can use
|
|
35
35
|
* @param modelTier - Model tier for this agent (fast/balanced/powerful)
|
|
36
36
|
*/
|
|
37
|
-
agentFactory: (config: Partial<AgentConfig>, systemPromptAddition?: string, useMinimalSystemPrompt?: boolean, noTools?: boolean, toolFilter?: string[], modelTier?: ModelTier
|
|
37
|
+
agentFactory: (config: Partial<AgentConfig>, systemPromptAddition?: string, useMinimalSystemPrompt?: boolean, noTools?: boolean, toolFilter?: string[], modelTier?: ModelTier,
|
|
38
|
+
/** Which agent is being built — see TeamAgent.initialize. */
|
|
39
|
+
teamAgent?: TeamAgent) => Promise<Agent>;
|
|
38
40
|
/**
|
|
39
41
|
* Initial agents to add to the team (optional)
|
|
40
42
|
* If not provided, starts with just the default agent
|
package/dist/team/types.d.ts
CHANGED
|
@@ -137,6 +137,16 @@ export interface TeamAgentConfig {
|
|
|
137
137
|
toolProfile?: ToolProfile;
|
|
138
138
|
/** Groups behind a `custom` profile — see TeamAgent.customGroups. */
|
|
139
139
|
customGroups?: string[];
|
|
140
|
+
/**
|
|
141
|
+
* MCP servers this agent may reach, by server name.
|
|
142
|
+
*
|
|
143
|
+
* ⚠️ `undefined` AND `[]` MEAN DIFFERENT THINGS, DELIBERATELY. `undefined` is "never asked" —
|
|
144
|
+
* every agent serialized before per-agent grants existed, which reached every server, and
|
|
145
|
+
* still does. `[]` is "asked, and granted nothing", which is what a newly created agent gets.
|
|
146
|
+
* Collapsing them would either strip reach from agents that depend on it or hand every new
|
|
147
|
+
* agent the whole machine.
|
|
148
|
+
*/
|
|
149
|
+
mcpServers?: string[];
|
|
140
150
|
/**
|
|
141
151
|
* Enabled skills for this agent
|
|
142
152
|
* Empty array means all skills, non-empty means filtered
|
|
@@ -176,6 +186,8 @@ export interface SerializedTeamAgent {
|
|
|
176
186
|
toolFilter?: string[];
|
|
177
187
|
toolProfile?: ToolProfile;
|
|
178
188
|
customGroups?: string[];
|
|
189
|
+
/** See TeamAgentConfig.mcpServers — undefined means a pre-grants agent, which reaches all. */
|
|
190
|
+
mcpServers?: string[];
|
|
179
191
|
enabledSkills?: string[];
|
|
180
192
|
modelTier?: ModelTier;
|
|
181
193
|
autoApproveHandoff?: boolean;
|