@selesai/code 0.13.29 β 0.13.30
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/CHANGELOG.md +10 -0
- package/README.md +8 -2
- package/dist/core/model-registry.d.ts +13 -1
- package/dist/core/model-registry.js +16 -0
- package/dist/defaults/settings.json +8 -13
- package/dist/extensions/capability-gateway/catalog.ts +2 -2
- package/dist/extensions/capability-gateway/index.ts +103 -18
- package/dist/extensions/capability-gateway/integration.test.ts +394 -8
- package/dist/extensions/capability-gateway/routing.test.ts +415 -0
- package/dist/extensions/capability-gateway/routing.ts +221 -0
- package/dist/extensions/grep-app/index.ts +10 -0
- package/dist/extensions/jev/decisions.test.ts +316 -0
- package/dist/extensions/jev/decisions.ts +527 -0
- package/dist/extensions/jev/test-support.ts +233 -0
- package/dist/extensions/jev-advisory-lifecycle.test.ts +206 -0
- package/dist/extensions/jev-advisory-memory.test.ts +191 -0
- package/dist/extensions/jev-advisory-recommendations.test.ts +240 -0
- package/dist/extensions/jev-advisory-routing.ts +539 -0
- package/dist/extensions/package.json +2 -2
- package/dist/extensions/pi-hermes-memory/src/memory-search-bridge.ts +40 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +57 -46
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +17 -0
- package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +10 -0
- package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +5 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +25 -0
- package/dist/extensions/pi-intercom/index.ts +10 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +5 -0
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +10 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +10 -0
- package/dist/extensions/pi-web-agent/src/extension.ts +5 -0
- package/dist/extensions/question/index.ts +5 -0
- package/dist/extensions/tokenin-onboarding.ts +185 -0
- package/dist/skills/code-review-and-quality/SKILL.md +396 -0
- package/dist/skills/code-simplification/SKILL.md +331 -0
- package/dist/skills/incremental-implementation/SKILL.md +249 -0
- package/dist/skills/planning-and-task-breakdown/SKILL.md +257 -0
- package/dist/skills/references/agent-skills-LICENSE +21 -0
- package/dist/skills/references/definition-of-done.md +67 -0
- package/dist/skills/references/performance-checklist.md +236 -0
- package/dist/skills/references/security-checklist.md +248 -0
- package/docs/settings.md +68 -31
- package/package.json +3 -3
- package/dist/extensions/auto-model.test.ts +0 -438
- package/dist/extensions/auto-model.ts +0 -357
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@selesai/code` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.13.30] - 2026-09-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Four default skills from `addyosmani/agent-skills`.** `planning-and-task-breakdown`, `incremental-implementation`, `code-review-and-quality`, and `code-simplification` now ship as bundled skills and seed into the agent directory like the rest. Their shared reference files (`definition-of-done.md`, `security-checklist.md`, `performance-checklist.md`) ship in `src/skills/references/` with the upstream MIT license, and the links in the skills that cite them were rebased to that location.
|
|
9
|
+
- **Jev advisory routing (opt-in beta).** The new bundled `jev-advisory-routing` extension can make a bounded, read-only durable-memory lookup after an explicit memory cue, and suggest one discovered skill/workflow or a proportionate verification level. Both routes are disabled by default; neither writes memory, loads a skill, starts a workflow, runs a command, or weakens required checks.
|
|
10
|
+
- **Jev capability tie-breaking (opt-in beta).** The capability gateway can use Jev only to break an ambiguous lexical hint between two or three optional tools. It never receives the full catalog, tool schemas, or conversation history; unavailable, malformed, low-confidence, and `none` decisions preserve deterministic behavior.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **One Jev transport for every consumer.** Advisory routing and capability tie-breaking share bounded request validation and the Token-In decisions-model path. `jev-*` calls use Token-In's non-streaming endpoint and retain its rotateable-account failover.
|
|
14
|
+
|
|
5
15
|
## [0.13.29] - 2026-09-22
|
|
6
16
|
|
|
7
17
|
### Changed
|
package/README.md
CHANGED
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
Selesai is a terminal-first **AI coding agent** that takes software tasks from prompt to verified result. It is a maintained, extension-first fork of the [Pi coding agent](https://github.com/earendil-works/pi), with subagents, adaptive workflows, live research, recovery tools, a richer TUI, and **
|
|
16
|
+
Selesai is a terminal-first **AI coding agent** that takes software tasks from prompt to verified result. It is a maintained, extension-first fork of the [Pi coding agent](https://github.com/earendil-works/pi), with subagents, adaptive workflows, live research, recovery tools, a richer TUI, and **32 built-in skills** shipped together in one install.
|
|
17
17
|
|
|
18
18
|
Use your preferred model provider, connect a local or OpenAI-compatible endpoint, or get a ready-to-use token from **[token.in](https://token.selesai.in)**. Selesai keeps Pi's small, hackable core while reducing the setup and extension work needed before an agent can be productive.
|
|
19
19
|
|
|
20
20
|
| | |
|
|
21
21
|
| --- | --- |
|
|
22
|
-
| **Ready out of the box** | One package bundles coordinated extensions, tools, themes, and
|
|
22
|
+
| **Ready out of the box** | One package bundles coordinated extensions, tools, themes, and 32 skills across 9 meaningful categories. |
|
|
23
23
|
| **Delegates and parallelizes** | Focused subagents can research, review, build, or run independent workstreams with foreground, background, parallel, and chained execution. |
|
|
24
24
|
| **Adapts to the task** | The workflow chooses the smallest useful pathβfrom a direct edit to reconnaissance, implementation, review, fixes, and validation. |
|
|
25
25
|
| **Researches with current sources** | Built-in web research, public GitHub code search, and interactive questions help the agent resolve uncertainty before it acts. |
|
|
@@ -113,6 +113,8 @@ Skills ship inside `@selesai/code` and are discovered when Selesai starts. Invok
|
|
|
113
113
|
|
|
114
114
|
### π Planning & orchestration
|
|
115
115
|
|
|
116
|
+
- `planning-and-task-breakdown` β turn a spec into ordered, implementable tasks with acceptance criteria.
|
|
117
|
+
- `incremental-implementation` β land work in thin, verifiable slices with per-increment checks.
|
|
116
118
|
- `pi-subagents` β delegation patterns for parallel, asynchronous, and chained work.
|
|
117
119
|
|
|
118
120
|
### π§Ή Code simplicity β the ponytail family
|
|
@@ -127,6 +129,8 @@ Skills ship inside `@selesai/code` and are discovered when Selesai starts. Invok
|
|
|
127
129
|
### π οΈ Codebase quality
|
|
128
130
|
|
|
129
131
|
- `improve-codebase` β inspect and improve architecture, maintainability, and overall codebase quality.
|
|
132
|
+
- `code-review-and-quality` β five-axis review, severity labels, and change sizing before merge.
|
|
133
|
+
- `code-simplification` β remove accumulated complexity without changing behavior.
|
|
130
134
|
|
|
131
135
|
### β
Completion & output discipline
|
|
132
136
|
|
|
@@ -146,6 +150,8 @@ Skills ship inside `@selesai/code` and are discovered when Selesai starts. Invok
|
|
|
146
150
|
|
|
147
151
|
- `agent-browser` β browser and Electron automation, scraping, and QA workflows.
|
|
148
152
|
|
|
153
|
+
`planning-and-task-breakdown`, `incremental-implementation`, `code-review-and-quality`, and `code-simplification` are adapted from [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) (MIT). Their shared reference material ships in `src/skills/references/`, with the upstream license alongside it.
|
|
154
|
+
|
|
149
155
|
---
|
|
150
156
|
|
|
151
157
|
## What Selesai adds to Pi
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Api, AuthResult, Model, Provider } from "@earendil-works/pi-ai";
|
|
1
|
+
import type { Api, AssistantMessage, AssistantMessageEventStream, AuthResult, Context, Model, ModelsSimpleStreamOptions, Provider } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { AuthStorage } from "./auth-storage.ts";
|
|
3
3
|
import type { ModelRuntime } from "./model-runtime.ts";
|
|
4
4
|
import type { AuthStatus, ProviderConfigInput } from "./provider-composer.ts";
|
|
@@ -31,6 +31,18 @@ export declare class ModelRegistry {
|
|
|
31
31
|
hasConfiguredAuth(model: Model<Api>): boolean;
|
|
32
32
|
getApiKeyAndHeaders(model: Model<Api>): Promise<ResolvedRequestAuth>;
|
|
33
33
|
getProviderAuthStatus(provider: string): AuthStatus;
|
|
34
|
+
/**
|
|
35
|
+
* Run a request through the composed provider layer β the same path the agent
|
|
36
|
+
* uses, so a provider's `streamSimple` override, auth resolution, base-URL
|
|
37
|
+
* overrides, and header transforms all apply.
|
|
38
|
+
*
|
|
39
|
+
* Extensions that call a model directly should use this instead of pi-ai's
|
|
40
|
+
* compat dispatch, which knows nothing about the providers this runtime
|
|
41
|
+
* composes (a provider override registered here is invisible to it).
|
|
42
|
+
*/
|
|
43
|
+
streamSimple(model: Model<Api>, context: Context, options?: ModelsSimpleStreamOptions): AssistantMessageEventStream;
|
|
44
|
+
/** One non-streaming request through the composed provider layer. */
|
|
45
|
+
complete(model: Model<Api>, context: Context, options?: ModelsSimpleStreamOptions): Promise<AssistantMessage>;
|
|
34
46
|
getProvider(provider: string): Provider | undefined;
|
|
35
47
|
getProviderDisplayName(provider: string): string;
|
|
36
48
|
getProviderAuth(provider: string): Promise<AuthResult | undefined>;
|
|
@@ -63,6 +63,22 @@ export class ModelRegistry {
|
|
|
63
63
|
getProviderAuthStatus(provider) {
|
|
64
64
|
return this.runtime.getProviderAuthStatus(provider);
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Run a request through the composed provider layer β the same path the agent
|
|
68
|
+
* uses, so a provider's `streamSimple` override, auth resolution, base-URL
|
|
69
|
+
* overrides, and header transforms all apply.
|
|
70
|
+
*
|
|
71
|
+
* Extensions that call a model directly should use this instead of pi-ai's
|
|
72
|
+
* compat dispatch, which knows nothing about the providers this runtime
|
|
73
|
+
* composes (a provider override registered here is invisible to it).
|
|
74
|
+
*/
|
|
75
|
+
streamSimple(model, context, options) {
|
|
76
|
+
return this.runtime.streamSimple(model, context, options);
|
|
77
|
+
}
|
|
78
|
+
/** One non-streaming request through the composed provider layer. */
|
|
79
|
+
complete(model, context, options) {
|
|
80
|
+
return this.runtime.complete(model, context, options);
|
|
81
|
+
}
|
|
66
82
|
getProvider(provider) {
|
|
67
83
|
return this.runtime.getProvider(provider);
|
|
68
84
|
}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"autoModel": {
|
|
3
|
-
"enabled": false,
|
|
4
|
-
"classifier": {
|
|
5
|
-
"provider": "tokenin",
|
|
6
|
-
"model": "jev-1.13"
|
|
7
|
-
},
|
|
8
|
-
"tiers": {
|
|
9
|
-
"simple": "tokenin/deepseek-v4.1-flash",
|
|
10
|
-
"medium": "tokenin/celestial-pro",
|
|
11
|
-
"complex": "tokenin/celestial-max",
|
|
12
|
-
"reasoning": "tokenin/celestial-ultra"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
2
|
"autoHandoff": {
|
|
16
3
|
"enabled": true,
|
|
17
4
|
"thresholdTokens": 256000
|
|
18
5
|
},
|
|
6
|
+
"jevAdvisory": {
|
|
7
|
+
"provider": "tokenin",
|
|
8
|
+
"model": "jev-1.13",
|
|
9
|
+
"routes": {
|
|
10
|
+
"memory": { "enabled": false },
|
|
11
|
+
"recommendations": { "enabled": false }
|
|
12
|
+
}
|
|
13
|
+
},
|
|
19
14
|
"collapseChangelog": true,
|
|
20
15
|
"compaction": {
|
|
21
16
|
"enabled": true,
|
|
@@ -29,13 +29,13 @@ export function firstSentence(text: string): string {
|
|
|
29
29
|
return (end === -1 ? trimmed : trimmed.slice(0, end + 1)).trim();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/** Compact summary for a tool: explicit discovery metadata wins, then first sentence
|
|
32
|
+
/** Compact summary for a tool: explicit discovery metadata wins, then the description's first sentence. */
|
|
33
33
|
export function toolSummary(tool: ToolInfo): string {
|
|
34
34
|
const explicit = tool.discovery?.summary?.trim();
|
|
35
35
|
if (explicit) return explicit;
|
|
36
36
|
const sentence = firstSentence(tool.description);
|
|
37
37
|
if (sentence) return sentence;
|
|
38
|
-
return tool.
|
|
38
|
+
return tool.name;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function buildToolCatalog(tools: ToolInfo[], gatewayToolNames: Set<string>): CatalogEntry[] {
|
|
@@ -16,8 +16,19 @@
|
|
|
16
16
|
* - A deterministic router activates a uniquely matched tool before the run.
|
|
17
17
|
* Skills and ambiguous matches remain discoverable through the catalog
|
|
18
18
|
* without injecting fuzzy hints into the model context.
|
|
19
|
+
* - Opt-in Jev-assisted routing (capabilityGateway.routing.jev in settings.json)
|
|
20
|
+
* is only a bounded tie-breaker: when the deterministic router returns an
|
|
21
|
+
* ambiguous lexical hint among optional tools, the gateway offers just those
|
|
22
|
+
* hinted tools (two or three) and the current prompt to the Jev decisions
|
|
23
|
+
* model as one constrained choice question. Jev may answer `none` or one
|
|
24
|
+
* hinted canonical tool name; the gateway revalidates the choice against the
|
|
25
|
+
* live catalog and activates it for the current run only. No hint, a unique
|
|
26
|
+
* activation, a skill match, or an already-activated tool never reaches Jev.
|
|
27
|
+
* Every Jev failure is an ordinary abstention that leaves the deterministic
|
|
28
|
+
* behavior in place, so a missing Token-In subscription is invisible.
|
|
19
29
|
* - Temporary activations reset at agent_settled, restoring the baseline
|
|
20
|
-
* active-tool set
|
|
30
|
+
* active-tool set; a tool_execution_start for one of them records whether the
|
|
31
|
+
* activation was actually used (content-free tool name + source only).
|
|
21
32
|
* - The system-prompt skill index is replaced by a compact capability
|
|
22
33
|
* instruction; full skill instructions load only on explicit show/invoke.
|
|
23
34
|
* - Telemetry events are emitted on the shared event bus.
|
|
@@ -36,6 +47,14 @@ import {
|
|
|
36
47
|
route,
|
|
37
48
|
type CatalogEntry,
|
|
38
49
|
} from "./catalog.ts";
|
|
50
|
+
import {
|
|
51
|
+
hintedToolCandidates,
|
|
52
|
+
MIN_GATEWAY_JEV_CANDIDATES,
|
|
53
|
+
readGatewayJevConfig,
|
|
54
|
+
routeToJevTool,
|
|
55
|
+
JEV_UNAVAILABLE_REASONS,
|
|
56
|
+
} from "./routing.ts";
|
|
57
|
+
import { confidenceBucket } from "../jev/decisions.ts";
|
|
39
58
|
|
|
40
59
|
export const GATEWAY_ENV = "SELESAI_CAPABILITY_GATEWAY";
|
|
41
60
|
export const GATEWAY_TOOLS = new Set(["capability_catalog", "capability_discover", "capability_skill_show"]);
|
|
@@ -118,9 +137,25 @@ function emitTelemetry(pi: ExtensionAPI, event: string, data: Record<string, unk
|
|
|
118
137
|
}
|
|
119
138
|
}
|
|
120
139
|
|
|
140
|
+
/** Where a run-local tool activation came from; recorded so use telemetry can attribute it. */
|
|
141
|
+
type ActivationSource = "deterministic" | "jev" | "discover";
|
|
142
|
+
|
|
121
143
|
export default function capabilityGatewayExtension(pi: ExtensionAPI): void {
|
|
122
144
|
if (!isEnabled()) return;
|
|
123
145
|
|
|
146
|
+
// Tools this gateway activated for the current run, and whether they were invoked.
|
|
147
|
+
// Cleared at agent_settled with the activations themselves.
|
|
148
|
+
const activations = new Map<string, { source: ActivationSource; used: boolean }>();
|
|
149
|
+
|
|
150
|
+
/** Activate one tool for the current run, keeping the rest of the loadout untouched. */
|
|
151
|
+
function activateTool(name: string, source: ActivationSource): void {
|
|
152
|
+
const active = pi.getActiveTools();
|
|
153
|
+
if (!active.includes(name)) {
|
|
154
|
+
pi.setActiveTools([...active, name]);
|
|
155
|
+
}
|
|
156
|
+
activations.set(name, { source, used: false });
|
|
157
|
+
}
|
|
158
|
+
|
|
124
159
|
// ------------------------------------------------------------------
|
|
125
160
|
// Session start: snapshot baseline, make extension tools dormant, and
|
|
126
161
|
// install the compact skill index (action methods are stubs until bind).
|
|
@@ -182,7 +217,8 @@ export default function capabilityGatewayExtension(pi: ExtensionAPI): void {
|
|
|
182
217
|
})
|
|
183
218
|
: filtered;
|
|
184
219
|
const text = formatCatalog(shown);
|
|
185
|
-
|
|
220
|
+
// Content-free: catalog query and filter arguments never travel.
|
|
221
|
+
emitTelemetry(pi, "catalog", { results: shown.length });
|
|
186
222
|
return {
|
|
187
223
|
content: [{ type: "text", text }],
|
|
188
224
|
details: { count: shown.length, total: filtered.length },
|
|
@@ -220,10 +256,7 @@ export default function capabilityGatewayExtension(pi: ExtensionAPI): void {
|
|
|
220
256
|
details: { activated: false },
|
|
221
257
|
};
|
|
222
258
|
}
|
|
223
|
-
|
|
224
|
-
if (!active.includes(entry.name)) {
|
|
225
|
-
pi.setActiveTools([...active, entry.name]);
|
|
226
|
-
}
|
|
259
|
+
activateTool(entry.name, "discover");
|
|
227
260
|
emitTelemetry(pi, "discover", { tool: entry.name });
|
|
228
261
|
return {
|
|
229
262
|
content: [
|
|
@@ -280,32 +313,84 @@ export default function capabilityGatewayExtension(pi: ExtensionAPI): void {
|
|
|
280
313
|
});
|
|
281
314
|
|
|
282
315
|
// ------------------------------------------------------------------
|
|
283
|
-
//
|
|
284
|
-
//
|
|
316
|
+
// Routing: the deterministic catalog router first; opt-in Jev routing is
|
|
317
|
+
// only a bounded tie-breaker for its ambiguous/hint result.
|
|
285
318
|
// ------------------------------------------------------------------
|
|
286
|
-
pi.on("before_agent_start", (event) => {
|
|
287
|
-
const
|
|
288
|
-
|
|
319
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
320
|
+
const entries = catalogEntries(pi);
|
|
321
|
+
const result = routePrompt(event.prompt, entries);
|
|
289
322
|
if (result.action === "activate" && result.entry) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
323
|
+
activateTool(result.entry.name, "deterministic");
|
|
324
|
+
emitTelemetry(pi, "route", {
|
|
325
|
+
source: "deterministic",
|
|
326
|
+
outcome: "activated",
|
|
327
|
+
tool: result.entry.name,
|
|
328
|
+
candidates: 0,
|
|
329
|
+
durationMs: 0,
|
|
330
|
+
});
|
|
295
331
|
return undefined;
|
|
296
332
|
}
|
|
297
333
|
// Do not inject fuzzy recommendations or catalog hints into the model
|
|
298
334
|
// context. It can discover capabilities when it actually needs one.
|
|
335
|
+
// Jev is a tie-breaker, never a classifier: only the ambiguous/hint
|
|
336
|
+
// result counts. No lexical signal or unique skill recommendation
|
|
337
|
+
// reaches it, and skills are never offered as candidates.
|
|
338
|
+
if (result.action !== "hint") return undefined;
|
|
339
|
+
const config = readGatewayJevConfig();
|
|
340
|
+
if (!config.enabled) return undefined;
|
|
341
|
+
const candidates = hintedToolCandidates(result.candidates);
|
|
342
|
+
if (candidates.length < MIN_GATEWAY_JEV_CANDIDATES) return undefined;
|
|
343
|
+
|
|
344
|
+
emitTelemetry(pi, "route", { source: "jev", outcome: "attempt", candidates: candidates.length });
|
|
345
|
+
const jevRoute = await routeToJevTool(candidates, event.prompt, ctx, config);
|
|
346
|
+
if (!jevRoute.selected) {
|
|
347
|
+
emitTelemetry(pi, "route", {
|
|
348
|
+
source: "jev",
|
|
349
|
+
outcome: JEV_UNAVAILABLE_REASONS.has(jevRoute.reason) ? "unavailable" : "abstained",
|
|
350
|
+
reason: jevRoute.reason,
|
|
351
|
+
candidates: jevRoute.candidates,
|
|
352
|
+
durationMs: jevRoute.elapsedMs,
|
|
353
|
+
});
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Revalidate the accepted choice against the live catalog immediately
|
|
358
|
+
// before activation: a stale or no-longer-eligible name activates nothing.
|
|
359
|
+
const live = eligibleTools(pi).find((tool) => tool.name === jevRoute.tool);
|
|
360
|
+
if (live) activateTool(live.name, "jev");
|
|
361
|
+
emitTelemetry(pi, "route", {
|
|
362
|
+
source: "jev",
|
|
363
|
+
outcome: live ? "activated" : "abstained",
|
|
364
|
+
...(live ? { tool: live.name } : { tool: jevRoute.tool }),
|
|
365
|
+
confidence: confidenceBucket(jevRoute.confidence),
|
|
366
|
+
candidates: jevRoute.candidates,
|
|
367
|
+
durationMs: jevRoute.elapsedMs,
|
|
368
|
+
});
|
|
299
369
|
return undefined;
|
|
300
370
|
});
|
|
301
371
|
|
|
302
372
|
// ------------------------------------------------------------------
|
|
303
|
-
//
|
|
373
|
+
// Selected -> used: a run-local activation is "used" when its tool is
|
|
374
|
+
// actually executed before the run settles. Content-free: name + source.
|
|
375
|
+
// ------------------------------------------------------------------
|
|
376
|
+
pi.on("tool_execution_start", (event) => {
|
|
377
|
+
const activation = activations.get(event.toolName);
|
|
378
|
+
if (!activation || activation.used) return;
|
|
379
|
+
activation.used = true;
|
|
380
|
+
emitTelemetry(pi, "use", { tool: event.toolName, source: activation.source });
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// ------------------------------------------------------------------
|
|
384
|
+
// Reset: restore the baseline active-tool set after the run settles and
|
|
385
|
+
// report how many run-local activations were actually used.
|
|
304
386
|
// ------------------------------------------------------------------
|
|
305
387
|
pi.on("agent_settled", () => {
|
|
306
388
|
const baseline = pi.getActiveTools().filter((name) => !eligibleTools(pi).some((tool) => tool.name === name));
|
|
307
389
|
pi.setActiveTools(baseline);
|
|
308
|
-
|
|
390
|
+
let used = 0;
|
|
391
|
+
for (const activation of activations.values()) if (activation.used) used += 1;
|
|
392
|
+
emitTelemetry(pi, "reset", { activeCount: baseline.length, activated: activations.size, used });
|
|
393
|
+
activations.clear();
|
|
309
394
|
});
|
|
310
395
|
|
|
311
396
|
// ------------------------------------------------------------------
|