@fastagent-sh/fastagent 0.23.0 → 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/agent.d.ts +10 -0
- package/dist/channels/kit/preview-kit.d.ts +6 -1
- package/dist/channels/kit/preview-kit.js +21 -4
- package/dist/channels/slack/preview.js +2 -1
- package/dist/cli/commands/info.js +7 -7
- package/dist/cli/commands/login.js +2 -2
- package/dist/cli/commands/models.js +3 -3
- package/dist/cli/invoke-stream.js +14 -0
- package/dist/cli/shared.d.ts +2 -1
- package/dist/cli/shared.js +6 -7
- package/dist/deploy/preflight.js +19 -1
- package/dist/dev-supervisor.js +2 -2
- package/dist/engines/pi/agent-models.d.ts +14 -1
- package/dist/engines/pi/agent-models.js +35 -8
- package/dist/engines/pi/agent-session-factory.d.ts +13 -13
- package/dist/engines/pi/agent-session-factory.js +132 -159
- package/dist/engines/pi/auth.js +6 -94
- package/dist/engines/pi/create.d.ts +16 -9
- package/dist/engines/pi/create.js +71 -38
- package/dist/engines/pi/invoke-session.js +82 -16
- package/dist/engines/pi/locked-file.d.ts +9 -0
- package/dist/engines/pi/locked-file.js +102 -0
- package/dist/engines/pi/login.d.ts +7 -1
- package/dist/engines/pi/login.js +22 -6
- package/dist/engines/pi/machine.d.ts +23 -0
- package/dist/engines/pi/machine.js +24 -2
- package/dist/engines/pi/models.d.ts +47 -42
- package/dist/engines/pi/models.js +147 -67
- package/dist/engines/pi/open.d.ts +25 -11
- package/dist/engines/pi/open.js +42 -20
- package/dist/engines/pi/report.d.ts +3 -0
- package/dist/engines/pi/report.js +11 -0
- package/dist/engines/pi/session-builder.js +6 -22
- package/dist/engines/pi/session-control.d.ts +4 -4
- package/dist/engines/pi/session-control.js +72 -40
- package/dist/engines/pi/session-settings.d.ts +9 -0
- package/dist/engines/pi/session-settings.js +17 -0
- package/dist/engines/pi/session-store.d.ts +2 -2
- package/dist/engines/pi/tool-context.d.ts +3 -5
- package/dist/engines/pi/tool-context.js +4 -16
- package/dist/engines/pi/tool.d.ts +32 -21
- package/dist/engines/pi/tool.js +21 -25
- package/dist/engines/pi/turn-kit.d.ts +18 -9
- package/dist/engines/pi/turn-kit.js +104 -27
- package/dist/pi.d.ts +1 -1
- package/dist/pi.js +1 -1
- package/dist/session.d.ts +60 -8
- package/dist/session.js +5 -1
- package/package.json +5 -5
- package/dist/engines/pi/search-tools.d.ts +0 -4
- package/dist/engines/pi/search-tools.js +0 -109
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { type MountedTool } from "./tool.ts";
|
|
2
|
-
/** Mount the built-in loader iff any mounted tool is deferred and the author didn't define their own. */
|
|
3
|
-
export declare function withSearchTool(tools: MountedTool[]): MountedTool[];
|
|
4
|
-
export declare function makeSearchToolsTool(): MountedTool;
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/** The built-in `search_tools` loader — the discovery surface for deferred tools (defineTool `deferred: true`). */
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { log } from "../../log.js";
|
|
4
|
-
import { defineTool, isDeferredTool, stripDeferredMarker } from "./tool.js";
|
|
5
|
-
/** Mount the built-in loader iff any mounted tool is deferred and the author didn't define their own. */
|
|
6
|
-
export function withSearchTool(tools) {
|
|
7
|
-
if (!tools.some(isDeferredTool))
|
|
8
|
-
return tools;
|
|
9
|
-
const authored = tools.find((t) => t.name === "search_tools");
|
|
10
|
-
if (!authored)
|
|
11
|
-
return [...tools, makeSearchToolsTool()];
|
|
12
|
-
let fixed = authored;
|
|
13
|
-
if (isDeferredTool(fixed)) {
|
|
14
|
-
// A deferred LOADER is a contradiction — it is the only entry point to the deferred tools, so nothing could ever
|
|
15
|
-
// activate it (or, through it, them).
|
|
16
|
-
log.warn("[fastagent] search_tools is marked deferred — ignoring the marker: the loader must stay active, or no deferred tool could ever be activated");
|
|
17
|
-
fixed = stripDeferredMarker(fixed);
|
|
18
|
-
}
|
|
19
|
-
return fixed === authored ? tools : tools.map((t) => (t === authored ? fixed : t));
|
|
20
|
-
}
|
|
21
|
-
/** Activation cap per search: activation is additive, session-persisted, and has NO deactivate path. */
|
|
22
|
-
const MAX_ACTIVATIONS_PER_SEARCH = 5;
|
|
23
|
-
/** Miss-path listing cap — same rationale as the activation cap. */
|
|
24
|
-
const MAX_MISS_LISTING = 10;
|
|
25
|
-
export function makeSearchToolsTool() {
|
|
26
|
-
return defineTool({
|
|
27
|
-
name: "search_tools",
|
|
28
|
-
description:
|
|
29
|
-
// First line short on purpose: the base prompt's tools list truncates at the first newline, and the discovery
|
|
30
|
-
// guidance below would otherwise flood it (and duplicate its deferred note).
|
|
31
|
-
"Discover and activate additional tools.\n" +
|
|
32
|
-
"Part of this agent's toolset is inactive until needed: search by keywords (e.g. what you are " +
|
|
33
|
-
"trying to do), and matching inactive tools are activated and become callable from that point on " +
|
|
34
|
-
"(if too many match, you get the candidates back — narrow the query, or query an exact tool " +
|
|
35
|
-
"name). ALWAYS search here before concluding a capability is missing.",
|
|
36
|
-
input: z.object({
|
|
37
|
-
query: z.string().min(1).describe("keywords describing the capability you need (e.g. 'weather forecast')"),
|
|
38
|
-
}),
|
|
39
|
-
async execute(input, ctx) {
|
|
40
|
-
if (!ctx.tools)
|
|
41
|
-
return "tool activation is unavailable outside a conversation turn.";
|
|
42
|
-
// Search the WHOLE registered catalog: the loader is the only discovery surface, and in a long conversation the
|
|
43
|
-
// model does not remember what it activated.
|
|
44
|
-
// ponytail: naive keyword match (any query token as a case-insensitive substring of name+description) under a
|
|
45
|
-
// hard per-search activation cap — the two ceilings are relevance and irreversibility; swap in
|
|
46
|
-
// scoring/embeddings if catalogs outgrow this.
|
|
47
|
-
const tokens = input.query
|
|
48
|
-
.toLowerCase()
|
|
49
|
-
.split(/[^a-z0-9]+/)
|
|
50
|
-
.filter(Boolean);
|
|
51
|
-
// AND semantics: EVERY token must hit — "adding a word narrows" must actually hold, or the over-cap "narrow the
|
|
52
|
-
// query" instruction sends the model in circles (OR would widen with each word, and a shared prefix like
|
|
53
|
-
// "fetch" could make a whole tool family permanently over-cap).
|
|
54
|
-
const matchesQuery = (t) => {
|
|
55
|
-
const haystack = `${t.name} ${t.description}`.toLowerCase();
|
|
56
|
-
return tokens.every((token) => haystack.includes(token));
|
|
57
|
-
};
|
|
58
|
-
const describe = (t) => `${t.name} — ${t.description.split("\n")[0]}`;
|
|
59
|
-
const active = new Set(ctx.tools.active());
|
|
60
|
-
const registered = ctx.tools.registered();
|
|
61
|
-
// A query with no searchable tokens (all punctuation/symbols) must not match.
|
|
62
|
-
if (tokens.length === 0)
|
|
63
|
-
return `"${input.query}" contains no searchable keywords — describe the capability you need.`;
|
|
64
|
-
// Exact-name shortcut: the guaranteed escape hatch from the cap — a query that IS an INACTIVE registered tool's
|
|
65
|
-
// name addresses that one tool, no keyword scoring in the way.
|
|
66
|
-
const exact = registered.find((t) => t.name.toLowerCase() === input.query.trim().toLowerCase());
|
|
67
|
-
const activeMatches = registered.filter((t) => active.has(t.name) && (t === exact || matchesQuery(t)));
|
|
68
|
-
const inactiveMatches = exact && !active.has(exact.name) ? [exact] : registered.filter((t) => !active.has(t.name) && matchesQuery(t));
|
|
69
|
-
// Same listing cap as every other branch — a wide token can match most of the ACTIVE set too (in chat that
|
|
70
|
-
// includes pi's default tools), and no answer may pour a catalog into the context.
|
|
71
|
-
const listedActive = activeMatches.slice(0, MAX_MISS_LISTING);
|
|
72
|
-
const moreActive = activeMatches.length - listedActive.length;
|
|
73
|
-
const activeNote = activeMatches.length > 0
|
|
74
|
-
? `Already active (call directly): ${listedActive.map(describe).join("; ")}${moreActive > 0 ? ` … and ${moreActive} more` : ""}.`
|
|
75
|
-
: "";
|
|
76
|
-
if (inactiveMatches.length === 0) {
|
|
77
|
-
if (activeNote)
|
|
78
|
-
return activeNote;
|
|
79
|
-
const inactive = registered.filter((t) => !active.has(t.name));
|
|
80
|
-
if (inactive.length === 0)
|
|
81
|
-
return "All tools are already active — nothing to discover.";
|
|
82
|
-
// Cap the miss listing like the activation cap — both guard the same semantic (don't pour the catalog back
|
|
83
|
-
// into the context the deferral exists to protect).
|
|
84
|
-
const listed = inactive.slice(0, MAX_MISS_LISTING);
|
|
85
|
-
const more = inactive.length - listed.length;
|
|
86
|
-
return `No tools matched "${input.query}". Inactive tools: ${listed.map(describe).join("; ")}${more > 0 ? ` … and ${more} more — search with different keywords.` : ""}`;
|
|
87
|
-
}
|
|
88
|
-
if (inactiveMatches.length > MAX_ACTIVATIONS_PER_SEARCH) {
|
|
89
|
-
// Same listing cap as the miss path — an over-cap answer must not pour the catalog into the context either.
|
|
90
|
-
const listed = inactiveMatches.slice(0, MAX_MISS_LISTING);
|
|
91
|
-
const more = inactiveMatches.length - listed.length;
|
|
92
|
-
return `${inactiveMatches.length} inactive tools matched "${input.query}" — too many to activate at once (activation is permanent for this conversation). Narrow the query (or query an exact name). Matches: ${listed
|
|
93
|
-
.map((t) => t.name)
|
|
94
|
-
.join(", ")}${more > 0 ? ` … and ${more} more` : ""}.${activeNote ? ` ${activeNote}` : ""}`;
|
|
95
|
-
}
|
|
96
|
-
const activated = ctx.tools.activate(inactiveMatches.map((t) => t.name));
|
|
97
|
-
// Report what actually happened, not what was attempted: a parallel sibling call may have activated the same
|
|
98
|
-
// matches first, leaving nothing new here.
|
|
99
|
-
if (activated.length === 0) {
|
|
100
|
-
return `Matched ${inactiveMatches.map((t) => t.name).join(", ")} — already active (possibly activated by a concurrent call). Call them directly.${activeNote ? ` ${activeNote}` : ""}`;
|
|
101
|
-
}
|
|
102
|
-
const raced = inactiveMatches.filter((t) => !activated.includes(t.name));
|
|
103
|
-
return `Activated: ${inactiveMatches
|
|
104
|
-
.filter((t) => activated.includes(t.name))
|
|
105
|
-
.map(describe)
|
|
106
|
-
.join("; ")}.${raced.length > 0 ? ` Already active: ${raced.map((t) => t.name).join(", ")}.` : ""}${activeNote ? ` ${activeNote}` : ""} These tools are callable now.`;
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
}
|