@selesai/code 0.13.15 → 0.13.16
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 +7 -0
- package/dist/extensions/capability-gateway/index.ts +12 -1
- package/dist/extensions/capability-gateway/integration.test.ts +23 -0
- package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
- package/dist/extensions/pi-web-agent/src/types.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@selesai/code` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.13.16] - 2026-09-06
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Capability gateway inline-skill routing.** `$skill` references expanded by inline skills are no longer re-recommended through `capability_skill_show`; embedded `<skill>` blocks are excluded from prompt routing.
|
|
9
|
+
- **TokenIn search backend URL.** A custom `baseUrl` ending in `/v1` no longer produces a doubled `/v1/v1/search/…` path, and accounts with a blank `apiKey` are now treated as unconfigured instead of triggering doomed requests.
|
|
10
|
+
- **TokenIn search fallback typing.** Search fallback metadata can now reference any provider, matching actual failover behavior.
|
|
11
|
+
|
|
5
12
|
## [0.13.15] - 2026-09-06
|
|
6
13
|
|
|
7
14
|
### Added
|
|
@@ -62,6 +62,17 @@ function catalogEntries(pi: ExtensionAPI): CatalogEntry[] {
|
|
|
62
62
|
return [...tools, ...skills];
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
const EMBEDDED_SKILL_BLOCK = /<skill\s+name="([^"]+)"[^>]*>[\s\S]*?<\/skill>/gi;
|
|
66
|
+
|
|
67
|
+
function routePrompt(prompt: string, entries: CatalogEntry[]): ReturnType<typeof route> {
|
|
68
|
+
const loadedSkills = new Set([...prompt.matchAll(EMBEDDED_SKILL_BLOCK)].map((match) => match[1]!.toLowerCase()));
|
|
69
|
+
const query = prompt.replace(EMBEDDED_SKILL_BLOCK, " ");
|
|
70
|
+
return route(
|
|
71
|
+
query,
|
|
72
|
+
entries.filter((entry) => entry.kind !== "skill" || !loadedSkills.has(entry.name.toLowerCase())),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
65
76
|
function formatCatalog(entries: CatalogEntry[]): string {
|
|
66
77
|
const lines = entries.map(
|
|
67
78
|
(entry) =>
|
|
@@ -253,7 +264,7 @@ export default function capabilityGatewayExtension(pi: ExtensionAPI): void {
|
|
|
253
264
|
// ambiguity hints, or nothing for unrelated prompts.
|
|
254
265
|
// ------------------------------------------------------------------
|
|
255
266
|
pi.on("before_agent_start", (event) => {
|
|
256
|
-
const result =
|
|
267
|
+
const result = routePrompt(event.prompt, catalogEntries(pi));
|
|
257
268
|
if (result.action === "none") return undefined;
|
|
258
269
|
if (result.action === "activate" && result.entry) {
|
|
259
270
|
const active = pi.getActiveTools();
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
const EXTENSIONS_DIR = fileURLToPath(new URL("../../", import.meta.url));
|
|
17
17
|
const GATEWAY_DIR = fileURLToPath(new URL(".", import.meta.url));
|
|
18
18
|
const GREP_APP_DIR = fileURLToPath(new URL("../grep-app", import.meta.url));
|
|
19
|
+
const INLINE_SKILLS_FILE = fileURLToPath(new URL("../inline-skills.ts", import.meta.url));
|
|
19
20
|
|
|
20
21
|
interface Harness {
|
|
21
22
|
session: AgentSession;
|
|
@@ -212,4 +213,26 @@ describe("capability gateway integration", () => {
|
|
|
212
213
|
expect(result?.messages?.[0]?.content).toContain("research");
|
|
213
214
|
expect(h.session.systemPrompt).not.toContain("Research instructions body.");
|
|
214
215
|
});
|
|
216
|
+
|
|
217
|
+
it("does not recommend capability_skill_show for an inline-loaded $skill", async () => {
|
|
218
|
+
const h = await createGatewaySession({
|
|
219
|
+
enabled: true,
|
|
220
|
+
withSkill: true,
|
|
221
|
+
extensions: [GATEWAY_DIR, GREP_APP_DIR, INLINE_SKILLS_FILE],
|
|
222
|
+
});
|
|
223
|
+
harnesses.push(h);
|
|
224
|
+
const runner = h.session.extensionRunner;
|
|
225
|
+
const input = await runner.emitInput("Use $research now", undefined, "interactive", undefined);
|
|
226
|
+
expect(input.action).toBe("transform");
|
|
227
|
+
if (input.action !== "transform") throw new Error("inline skill was not expanded");
|
|
228
|
+
expect(input.text).toContain("Research instructions body.");
|
|
229
|
+
|
|
230
|
+
const result = await runner.emitBeforeAgentStart(
|
|
231
|
+
input.text,
|
|
232
|
+
undefined,
|
|
233
|
+
h.session.systemPrompt,
|
|
234
|
+
{ cwd: process.cwd() } as never,
|
|
235
|
+
);
|
|
236
|
+
expect(result).toBeUndefined();
|
|
237
|
+
});
|
|
215
238
|
});
|
|
@@ -19,9 +19,11 @@ export function hasActiveTokenInAccount(authPath: string = join(getAgentDir(), '
|
|
|
19
19
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return false;
|
|
20
20
|
const auth = parsed as { accounts?: unknown; activeId?: unknown };
|
|
21
21
|
if (!Array.isArray(auth.accounts) || typeof auth.activeId !== 'string') return false;
|
|
22
|
-
return auth.accounts.some(
|
|
23
|
-
(a
|
|
24
|
-
|
|
22
|
+
return auth.accounts.some((a) => {
|
|
23
|
+
if (!a || typeof a !== 'object' || Array.isArray(a)) return false;
|
|
24
|
+
const account = a as { id?: unknown; apiKey?: unknown };
|
|
25
|
+
return account.id === auth.activeId && typeof account.apiKey === 'string' && account.apiKey.trim() !== '';
|
|
26
|
+
});
|
|
25
27
|
} catch {
|
|
26
28
|
return false;
|
|
27
29
|
}
|
|
@@ -27,7 +27,9 @@ export function readActiveTokenInAccount(
|
|
|
27
27
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;
|
|
28
28
|
const auth = parsed as TokenInAuth;
|
|
29
29
|
if (!Array.isArray(auth.accounts) || typeof auth.activeId !== 'string') return undefined;
|
|
30
|
-
return auth.accounts.find(
|
|
30
|
+
return auth.accounts.find(
|
|
31
|
+
(a) => a && typeof a === 'object' && a.id === auth.activeId && typeof a.apiKey === 'string' && a.apiKey.trim() !== ''
|
|
32
|
+
);
|
|
31
33
|
} catch {
|
|
32
34
|
return undefined;
|
|
33
35
|
}
|
|
@@ -82,8 +84,10 @@ export function createTokenInSearchTool({
|
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
+
const rootUrl = (account.baseUrl ?? TOKENIN_DEFAULT_BASE_URL)
|
|
88
|
+
.replace(/\/+$/, '')
|
|
89
|
+
.replace(/\/v1$/, '');
|
|
90
|
+
const url = `${rootUrl}/v1/search/${TOKENIN_SEARCH_TOOL_NAME}`;
|
|
87
91
|
|
|
88
92
|
try {
|
|
89
93
|
const response = await fetchImpl(url, {
|
|
@@ -34,7 +34,7 @@ export type ToolError = {
|
|
|
34
34
|
export type SearchMetadata = {
|
|
35
35
|
backend: SearchProviderName;
|
|
36
36
|
cacheHit: boolean;
|
|
37
|
-
fallbackFrom?:
|
|
37
|
+
fallbackFrom?: SearchProviderName;
|
|
38
38
|
fallbackReason?: string;
|
|
39
39
|
fanout?: FanoutMetadata;
|
|
40
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selesai/code",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.16",
|
|
4
4
|
"description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|