@senpi/runtime 3.0.79 → 3.0.81
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/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/telemetry/live-telemetry.d.ts +7 -2
- package/dist/telemetry/live-telemetry.d.ts.map +1 -1
- package/dist/telemetry/live-telemetry.js +156 -46
- package/dist/telemetry/live-telemetry.js.map +1 -1
- package/dist/widgets/errors.d.ts +23 -0
- package/dist/widgets/errors.d.ts.map +1 -0
- package/dist/widgets/errors.js +33 -0
- package/dist/widgets/errors.js.map +1 -0
- package/dist/widgets/index.d.ts +39 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +36 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/registry.d.ts +24 -0
- package/dist/widgets/registry.d.ts.map +1 -0
- package/dist/widgets/registry.js +26 -0
- package/dist/widgets/registry.js.map +1 -0
- package/dist/widgets/strategy-lookup.d.ts +39 -0
- package/dist/widgets/strategy-lookup.d.ts.map +1 -0
- package/dist/widgets/strategy-lookup.js +70 -0
- package/dist/widgets/strategy-lookup.js.map +1 -0
- package/dist/widgets/tool.d.ts +51 -0
- package/dist/widgets/tool.d.ts.map +1 -0
- package/dist/widgets/tool.js +250 -0
- package/dist/widgets/tool.js.map +1 -0
- package/dist/widgets/types.d.ts +91 -0
- package/dist/widgets/types.d.ts.map +1 -0
- package/dist/widgets/types.js +10 -0
- package/dist/widgets/types.js.map +1 -0
- package/dist/widgets/widgets/fund-strategy-wallet.d.ts +13 -0
- package/dist/widgets/widgets/fund-strategy-wallet.d.ts.map +1 -0
- package/dist/widgets/widgets/fund-strategy-wallet.js +139 -0
- package/dist/widgets/widgets/fund-strategy-wallet.js.map +1 -0
- package/dist/widgets/widgets/fund-user-wallet.d.ts +13 -0
- package/dist/widgets/widgets/fund-user-wallet.d.ts.map +1 -0
- package/dist/widgets/widgets/fund-user-wallet.js +78 -0
- package/dist/widgets/widgets/fund-user-wallet.js.map +1 -0
- package/openclaw.plugin.json +16 -1
- package/package.json +3 -2
- package/skills/senpi-widgets/SKILL.md +167 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounds one validation lookup. The user is waiting on a card, and the tool
|
|
3
|
+
* result is what unblocks the turn, so a backend that never answers has to
|
|
4
|
+
* become an error the model can retry rather than a hung turn.
|
|
5
|
+
*/
|
|
6
|
+
export const STRATEGY_LOOKUP_TIMEOUT_MS = 10_000;
|
|
7
|
+
/**
|
|
8
|
+
* Bounds the whole lookup, not just the MCP request.
|
|
9
|
+
*
|
|
10
|
+
* The signal passed to `listStrategies` covers the request only. Reaching it
|
|
11
|
+
* means first connecting a client, and the MCP SDK gives `initialize` its own
|
|
12
|
+
* 60s default — so an unreachable backend held the agent turn for over a minute
|
|
13
|
+
* while this module advertised a ten-second bound. The race is what makes the
|
|
14
|
+
* bound true; the signal stays so the in-flight request is really cancelled.
|
|
15
|
+
*/
|
|
16
|
+
async function withDeadline(work, ms) {
|
|
17
|
+
let timer;
|
|
18
|
+
const deadline = new Promise((_resolve, reject) => {
|
|
19
|
+
timer = setTimeout(() => reject(new Error(`strategy lookup timed out after ${ms}ms`)), ms);
|
|
20
|
+
});
|
|
21
|
+
try {
|
|
22
|
+
return await Promise.race([work, deadline]);
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const WALLET_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
29
|
+
export function isWalletAddress(value) {
|
|
30
|
+
return WALLET_ADDRESS_RE.test(value);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Address → strategy, over `strategy_list` with an address filter.
|
|
34
|
+
*
|
|
35
|
+
* Deliberately not `SenpiClient.getStrategy`: that one falls back to the first
|
|
36
|
+
* row when no row matches the requested address, and drops the address from its
|
|
37
|
+
* result, so a backend that ignored the filter would resolve any address to
|
|
38
|
+
* some strategy. Both are fine for a details read and fatal for a gate, so this
|
|
39
|
+
* matches the address itself and never falls back.
|
|
40
|
+
*
|
|
41
|
+
* `listStrategies` already fails closed — an unrecognizable response throws
|
|
42
|
+
* instead of reading as an empty list — so an empty result here is a real
|
|
43
|
+
* "no strategy of yours holds that address" and is returned as `null`.
|
|
44
|
+
*/
|
|
45
|
+
export function createSenpiStrategyLookup(withClient) {
|
|
46
|
+
return {
|
|
47
|
+
async findByWallet(wallet) {
|
|
48
|
+
// Lowercased for the backend, not just for the comparison below. The
|
|
49
|
+
// `strategy_addresses` filter is an exact SQL `in` against a column
|
|
50
|
+
// written lowercase, so a checksummed address matches nothing and comes
|
|
51
|
+
// back as an ordinary empty list — a correct address reported as "not
|
|
52
|
+
// yours", which is the one answer this gate must never invent.
|
|
53
|
+
const wanted = wallet.toLowerCase();
|
|
54
|
+
const rows = await withDeadline(withClient((client) => client.listStrategies({ strategyAddresses: [wanted] }, { signal: AbortSignal.timeout(STRATEGY_LOOKUP_TIMEOUT_MS) })), STRATEGY_LOOKUP_TIMEOUT_MS);
|
|
55
|
+
const matches = rows.filter((row) => row.strategyWalletAddress.toLowerCase() === wanted);
|
|
56
|
+
// An address may carry more than one row over its lifetime; a live
|
|
57
|
+
// strategy on it is the answer, and its status is what the gate judges.
|
|
58
|
+
const match = matches.find((row) => row.status.toUpperCase() === "ACTIVE") ?? matches[0];
|
|
59
|
+
if (!match)
|
|
60
|
+
return null;
|
|
61
|
+
return {
|
|
62
|
+
id: match.id,
|
|
63
|
+
walletAddress: match.strategyWalletAddress,
|
|
64
|
+
status: match.status,
|
|
65
|
+
name: match.strategyName,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=strategy-lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy-lookup.js","sourceRoot":"","sources":["../../src/widgets/strategy-lookup.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAEjD;;;;;;;;GAQG;AACH,KAAK,UAAU,YAAY,CAAI,IAAgB,EAAE,EAAU;IACzD,IAAI,KAAgD,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QACvD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mCAAmC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC9C,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEhD,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAkBD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAAyE;IAEzE,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,qEAAqE;YACrE,oEAAoE;YACpE,wEAAwE;YACxE,sEAAsE;YACtE,+DAA+D;YAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,YAAY,CAC7B,UAAU,CAAC,CAAC,MAAM,EAAE,EAAE,CACpB,MAAM,CAAC,cAAc,CACnB,EAAE,iBAAiB,EAAE,CAAC,MAAM,CAAC,EAAE,EAC/B,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,0BAA0B,CAAC,EAAE,CAC5D,CACF,EACD,0BAA0B,CAC3B,CAAC;YACF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,qBAAqB,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;YACzF,mEAAmE;YACnE,wEAAwE;YACxE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;YACzF,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YACxB,OAAO;gBACL,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,aAAa,EAAE,KAAK,CAAC,qBAAqB;gBAC1C,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,IAAI,EAAE,KAAK,CAAC,YAAY;aACzB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { WidgetRegistry } from "./registry.js";
|
|
2
|
+
import type { StrategyLookup, WidgetLogger, WidgetPayload } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The plugin's single agent tool. The name is static — new widget types are
|
|
5
|
+
* new `widget_type` enum values, never new tools — so it is declared once in
|
|
6
|
+
* `openclaw.plugin.json` under `contracts.tools`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SHOW_WIDGET_TOOL_NAME = "show_widget";
|
|
9
|
+
export interface ShowWidgetToolResult {
|
|
10
|
+
content: Array<{
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}>;
|
|
14
|
+
details: WidgetPayload;
|
|
15
|
+
}
|
|
16
|
+
export interface ShowWidgetTool {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
parameters: Record<string, unknown>;
|
|
20
|
+
execute(toolCallId: string, params: unknown): Promise<ShowWidgetToolResult>;
|
|
21
|
+
}
|
|
22
|
+
export interface CreateShowWidgetToolOptions {
|
|
23
|
+
registry: WidgetRegistry;
|
|
24
|
+
logger?: WidgetLogger;
|
|
25
|
+
/** Ephemeral session id from the plugin tool factory context; logged only. */
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
/** Backend a widget validates addresses against. Absent = those widgets fail closed. */
|
|
28
|
+
strategies?: StrategyLookup;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Tool description, generated from the enabled specs. This is the text the
|
|
32
|
+
* model selects a `widget_type` on, so it carries the per-type field list that
|
|
33
|
+
* the flat JSON schema cannot express.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildShowWidgetDescription(registry: WidgetRegistry): string;
|
|
36
|
+
/**
|
|
37
|
+
* Declared JSON schema: one flat object, no unions at any depth.
|
|
38
|
+
*
|
|
39
|
+
* OpenClaw flattens top-level unions, Gemini flattens nested ones, and OpenAI
|
|
40
|
+
* strict mode rejects a schema containing one anywhere — so a discriminated
|
|
41
|
+
* union on `widget_type` would buy nothing. Only `widget_type` is required;
|
|
42
|
+
* per-widget requiredness is enforced in `execute()`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildShowWidgetParameters(registry: WidgetRegistry): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Build the `show_widget` tool for one agent turn. `sessionId` comes from the
|
|
47
|
+
* plugin tool factory context and is used for logging only — nothing about the
|
|
48
|
+
* result depends on it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createShowWidgetTool(options: CreateShowWidgetToolOptions): ShowWidgetTool;
|
|
51
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/widgets/tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAEV,cAAc,EAEd,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAInD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC7E;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAQD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAuB3E;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyD3F;AAyID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,cAAc,CA2CzF"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { WidgetLookupError, WidgetValidationError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* The plugin's single agent tool. The name is static — new widget types are
|
|
4
|
+
* new `widget_type` enum values, never new tools — so it is declared once in
|
|
5
|
+
* `openclaw.plugin.json` under `contracts.tools`.
|
|
6
|
+
*/
|
|
7
|
+
export const SHOW_WIDGET_TOOL_NAME = "show_widget";
|
|
8
|
+
const WIDGET_TYPE_PARAM = "widget_type";
|
|
9
|
+
function fieldLine(field) {
|
|
10
|
+
const parts = [`${field.required ? "required" : "optional"} ${field.type}`];
|
|
11
|
+
if (field.maxLength !== undefined)
|
|
12
|
+
parts.push(`max ${field.maxLength} chars`);
|
|
13
|
+
return ` ${field.name} (${parts.join(", ")}): ${field.description}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Tool description, generated from the enabled specs. This is the text the
|
|
17
|
+
* model selects a `widget_type` on, so it carries the per-type field list that
|
|
18
|
+
* the flat JSON schema cannot express.
|
|
19
|
+
*/
|
|
20
|
+
export function buildShowWidgetDescription(registry) {
|
|
21
|
+
const widgets = registry.enabled();
|
|
22
|
+
const lines = [
|
|
23
|
+
"Render a Senpi UI card in the user's chat, for cases where a card communicates better than text.",
|
|
24
|
+
"",
|
|
25
|
+
`Pass ${WIDGET_TYPE_PARAM} plus only the fields listed for that type. Any other field is rejected.`,
|
|
26
|
+
"",
|
|
27
|
+
];
|
|
28
|
+
if (widgets.length === 0) {
|
|
29
|
+
lines.push("No widget types are enabled on this deployment.");
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
32
|
+
lines.push("Widget types:");
|
|
33
|
+
for (const widget of widgets) {
|
|
34
|
+
lines.push(`- ${widget.widgetType}: ${widget.description}`);
|
|
35
|
+
lines.push(" fields:");
|
|
36
|
+
if (widget.fields.length === 0) {
|
|
37
|
+
lines.push(" none — pass widget_type only");
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
lines.push(...widget.fields.map(fieldLine));
|
|
41
|
+
}
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Declared JSON schema: one flat object, no unions at any depth.
|
|
46
|
+
*
|
|
47
|
+
* OpenClaw flattens top-level unions, Gemini flattens nested ones, and OpenAI
|
|
48
|
+
* strict mode rejects a schema containing one anywhere — so a discriminated
|
|
49
|
+
* union on `widget_type` would buy nothing. Only `widget_type` is required;
|
|
50
|
+
* per-widget requiredness is enforced in `execute()`.
|
|
51
|
+
*/
|
|
52
|
+
export function buildShowWidgetParameters(registry) {
|
|
53
|
+
const widgets = registry.enabled();
|
|
54
|
+
if (widgets.length === 0) {
|
|
55
|
+
throw new Error("show_widget has no enabled widget types; do not register the tool");
|
|
56
|
+
}
|
|
57
|
+
const properties = {
|
|
58
|
+
[WIDGET_TYPE_PARAM]: {
|
|
59
|
+
type: "string",
|
|
60
|
+
enum: widgets.map((w) => w.widgetType),
|
|
61
|
+
description: "Which widget to render.",
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
// Fields shared by several widget types collapse into one property, so the
|
|
65
|
+
// shared property has to admit every owner's input: per-widget limits are
|
|
66
|
+
// enforced in execute(), and a declared bound stricter than one owner's would
|
|
67
|
+
// make the provider reject a call that execute() would have accepted.
|
|
68
|
+
const owners = new Map();
|
|
69
|
+
for (const widget of widgets) {
|
|
70
|
+
for (const field of widget.fields) {
|
|
71
|
+
if (field.name === WIDGET_TYPE_PARAM) {
|
|
72
|
+
throw new Error(`show_widget: ${widget.widgetType} declares a field named "${WIDGET_TYPE_PARAM}", which is the discriminator`);
|
|
73
|
+
}
|
|
74
|
+
const entry = owners.get(field.name);
|
|
75
|
+
if (entry) {
|
|
76
|
+
entry.types.push(widget.widgetType);
|
|
77
|
+
entry.fields.push(field);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
owners.set(field.name, { types: [widget.widgetType], fields: [field] });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const [name, { types, fields }] of owners) {
|
|
84
|
+
const valueTypes = [...new Set(fields.map((f) => f.type))];
|
|
85
|
+
if (valueTypes.length > 1) {
|
|
86
|
+
throw new Error(`show_widget: field "${name}" is declared as ${valueTypes.join(" and ")} by ${types.join(", ")}; one schema property cannot be both`);
|
|
87
|
+
}
|
|
88
|
+
const property = { type: valueTypes[0] };
|
|
89
|
+
const bounds = fields.map((f) => f.maxLength);
|
|
90
|
+
if (bounds.every((b) => b !== undefined)) {
|
|
91
|
+
property.maxLength = Math.max(...bounds);
|
|
92
|
+
}
|
|
93
|
+
property.description =
|
|
94
|
+
types.length === 1
|
|
95
|
+
? `${fields[0].description} Accepted by: ${types[0]}.`
|
|
96
|
+
: fields.map((f, i) => `${types[i]}: ${f.description}`).join(" ");
|
|
97
|
+
properties[name] = property;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties,
|
|
102
|
+
required: [WIDGET_TYPE_PARAM],
|
|
103
|
+
additionalProperties: false,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function availableTypesSentence(registry) {
|
|
107
|
+
const types = registry.enabledTypes();
|
|
108
|
+
return types.length === 0
|
|
109
|
+
? "No widget types are available."
|
|
110
|
+
: `Available widget types: ${types.join(", ")}.`;
|
|
111
|
+
}
|
|
112
|
+
/** Anything that is not a plain object carries no fields — treat it as empty. */
|
|
113
|
+
function toArgs(params) {
|
|
114
|
+
return params && typeof params === "object" && !Array.isArray(params)
|
|
115
|
+
? params
|
|
116
|
+
: {};
|
|
117
|
+
}
|
|
118
|
+
function resolveWidget(registry, args) {
|
|
119
|
+
const raw = args[WIDGET_TYPE_PARAM];
|
|
120
|
+
const available = availableTypesSentence(registry);
|
|
121
|
+
if (raw === undefined || raw === null) {
|
|
122
|
+
throw new WidgetValidationError(`show_widget failed: "${WIDGET_TYPE_PARAM}" is required. ${available}`, "(none)", [WIDGET_TYPE_PARAM]);
|
|
123
|
+
}
|
|
124
|
+
if (typeof raw !== "string") {
|
|
125
|
+
throw new WidgetValidationError(`show_widget failed: "${WIDGET_TYPE_PARAM}" must be a string. ${available}`, "(none)", [WIDGET_TYPE_PARAM]);
|
|
126
|
+
}
|
|
127
|
+
const widget = registry.get(raw);
|
|
128
|
+
if (!widget) {
|
|
129
|
+
throw new WidgetValidationError(`show_widget failed: unknown ${WIDGET_TYPE_PARAM} "${raw}". ${available}`, raw, [WIDGET_TYPE_PARAM]);
|
|
130
|
+
}
|
|
131
|
+
if (!widget.enabled) {
|
|
132
|
+
throw new WidgetValidationError(`show_widget failed: ${WIDGET_TYPE_PARAM} "${raw}" is not enabled on this deployment. ${available}`, raw, [WIDGET_TYPE_PARAM]);
|
|
133
|
+
}
|
|
134
|
+
return widget;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Validate the remaining args against the widget's own field list. Every
|
|
138
|
+
* problem is reported in one message so the model can fix them all in a single
|
|
139
|
+
* retry instead of discovering them one call at a time.
|
|
140
|
+
*/
|
|
141
|
+
function validateFields(widget, args) {
|
|
142
|
+
// Guarded fields are accepted as keys so the widget can answer them itself;
|
|
143
|
+
// they are absent from the description and the declared schema.
|
|
144
|
+
const guarded = widget.guardedFields ?? [];
|
|
145
|
+
const allowed = new Set([
|
|
146
|
+
WIDGET_TYPE_PARAM,
|
|
147
|
+
...widget.fields.map((f) => f.name),
|
|
148
|
+
...guarded.map((f) => f.name),
|
|
149
|
+
]);
|
|
150
|
+
const problems = [];
|
|
151
|
+
const offending = [];
|
|
152
|
+
const unexpected = Object.keys(args).filter((key) => !allowed.has(key));
|
|
153
|
+
if (unexpected.length > 0) {
|
|
154
|
+
offending.push(...unexpected);
|
|
155
|
+
// Guarded names are parseable but not on offer, so they stay out of this list.
|
|
156
|
+
const offered = [WIDGET_TYPE_PARAM, ...widget.fields.map((f) => f.name)];
|
|
157
|
+
problems.push(`unexpected fields: ${unexpected.join(", ")} (allowed: ${offered.join(", ")})`);
|
|
158
|
+
}
|
|
159
|
+
const missing = [];
|
|
160
|
+
const invalid = [];
|
|
161
|
+
const values = {};
|
|
162
|
+
// A guarded field goes to the handler exactly as it arrived. Its answer is
|
|
163
|
+
// "drop this field", which is the same for every value, so complaining about
|
|
164
|
+
// the type or the length first would only buy a retry that corrects a field
|
|
165
|
+
// the model then has to remove anyway.
|
|
166
|
+
for (const field of guarded) {
|
|
167
|
+
const value = Object.hasOwn(args, field.name) ? args[field.name] : undefined;
|
|
168
|
+
if (value !== undefined && value !== null)
|
|
169
|
+
values[field.name] = value;
|
|
170
|
+
}
|
|
171
|
+
for (const field of widget.fields) {
|
|
172
|
+
// Own keys only: an inherited `reason`/`toString` must not read as a value
|
|
173
|
+
// the caller passed, since the unexpected-key check above only sees own keys.
|
|
174
|
+
const value = Object.hasOwn(args, field.name) ? args[field.name] : undefined;
|
|
175
|
+
if (value === undefined || value === null) {
|
|
176
|
+
if (field.required)
|
|
177
|
+
missing.push(field.name);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (typeof value !== "string") {
|
|
181
|
+
invalid.push(`${field.name} (must be a ${field.type})`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (value.trim().length === 0) {
|
|
185
|
+
invalid.push(`${field.name} (must be a non-empty ${field.type}; omit it instead)`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (field.maxLength !== undefined && value.length > field.maxLength) {
|
|
189
|
+
invalid.push(`${field.name} (${value.length} chars, limit is ${field.maxLength})`);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
values[field.name] = value;
|
|
193
|
+
}
|
|
194
|
+
if (missing.length > 0) {
|
|
195
|
+
offending.push(...missing);
|
|
196
|
+
problems.push(`missing required fields: ${missing.join(", ")}`);
|
|
197
|
+
}
|
|
198
|
+
if (invalid.length > 0) {
|
|
199
|
+
offending.push(...invalid.map((entry) => entry.split(" ")[0]));
|
|
200
|
+
problems.push(`invalid fields: ${invalid.join(", ")}`);
|
|
201
|
+
}
|
|
202
|
+
if (problems.length > 0) {
|
|
203
|
+
throw new WidgetValidationError(`show_widget failed for ${WIDGET_TYPE_PARAM} "${widget.widgetType}": ${problems.join("; ")}. ` +
|
|
204
|
+
"Fix the named fields and call show_widget again with the same widget_type.", widget.widgetType, offending);
|
|
205
|
+
}
|
|
206
|
+
return values;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Build the `show_widget` tool for one agent turn. `sessionId` comes from the
|
|
210
|
+
* plugin tool factory context and is used for logging only — nothing about the
|
|
211
|
+
* result depends on it.
|
|
212
|
+
*/
|
|
213
|
+
export function createShowWidgetTool(options) {
|
|
214
|
+
const { registry, logger, sessionId, strategies } = options;
|
|
215
|
+
const session = sessionId ?? "(unknown)";
|
|
216
|
+
const ctx = { strategies };
|
|
217
|
+
return {
|
|
218
|
+
name: SHOW_WIDGET_TOOL_NAME,
|
|
219
|
+
description: buildShowWidgetDescription(registry),
|
|
220
|
+
parameters: buildShowWidgetParameters(registry),
|
|
221
|
+
async execute(_toolCallId, params) {
|
|
222
|
+
try {
|
|
223
|
+
const args = toArgs(params);
|
|
224
|
+
const widget = resolveWidget(registry, args);
|
|
225
|
+
const values = validateFields(widget, args);
|
|
226
|
+
const result = await widget.handler(values, ctx);
|
|
227
|
+
logger?.info(`[senpi-widgets] widget shown | widget_type=${widget.widgetType} widget_version=${widget.widgetVersion} session=${session}`);
|
|
228
|
+
return {
|
|
229
|
+
content: [{ type: "text", text: result.text }],
|
|
230
|
+
details: result.payload,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
if (err instanceof WidgetValidationError) {
|
|
235
|
+
logger?.warn(`[senpi-widgets] validation failed | widget_type=${err.widgetType} fields=${err.fields.join(",")} session=${session} | ${err.message}`);
|
|
236
|
+
}
|
|
237
|
+
else if (err instanceof WidgetLookupError) {
|
|
238
|
+
// The args were never judged — this is a backend problem, and it is the
|
|
239
|
+
// only signal that a card the user asked for could not be built.
|
|
240
|
+
logger?.warn(`[senpi-widgets] lookup failed | widget_type=${err.widgetType} session=${session} | ${err.message} | cause=${err.cause instanceof Error ? err.cause.message : String(err.cause ?? "(none)")}`);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
logger?.error?.(`[senpi-widgets] unhandled failure | session=${session} | ${err instanceof Error ? err.message : String(err)}`);
|
|
244
|
+
}
|
|
245
|
+
throw err;
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/widgets/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAUvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD,MAAM,iBAAiB,GAAG,aAAa,CAAC;AAuBxC,SAAS,SAAS,CAAC,KAAsB;IACvC,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5E,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,SAAS,QAAQ,CAAC,CAAC;IAC9E,OAAO,OAAO,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAwB;IACjE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG;QACZ,kGAAkG;QAClG,EAAE;QACF,QAAQ,iBAAiB,0EAA0E;QACnG,EAAE;KACH,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;YAC/C,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAwB;IAChE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;IACnC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,UAAU,GAA4B;QAC1C,CAAC,iBAAiB,CAAC,EAAE;YACnB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;YACtC,WAAW,EAAE,yBAAyB;SACvC;KACF,CAAC;IACF,2EAA2E;IAC3E,0EAA0E;IAC1E,8EAA8E;IAC9E,sEAAsE;IACtE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0D,CAAC;IACjF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBACrC,MAAM,IAAI,KAAK,CACb,gBAAgB,MAAM,CAAC,UAAU,4BAA4B,iBAAiB,+BAA+B,CAC9G,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACpC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC;QAC/C,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,uBAAuB,IAAI,oBAAoB,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,sCAAsC,CACrI,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAA4B,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC;YACzC,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAI,MAAmB,CAAC,CAAC;QACzD,CAAC;QACD,QAAQ,CAAC,WAAW;YAClB,KAAK,CAAC,MAAM,KAAK,CAAC;gBAChB,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,iBAAiB,KAAK,CAAC,CAAC,CAAC,GAAG;gBACtD,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtE,UAAU,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;IAC9B,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU;QACV,QAAQ,EAAE,CAAC,iBAAiB,CAAC;QAC7B,oBAAoB,EAAE,KAAK;KAC5B,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAwB;IACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC;IACtC,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;QACvB,CAAC,CAAC,gCAAgC;QAClC,CAAC,CAAC,2BAA2B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACrD,CAAC;AAED,iFAAiF;AACjF,SAAS,MAAM,CAAC,MAAe;IAC7B,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACnE,CAAC,CAAE,MAAkC;QACrC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,SAAS,aAAa,CACpB,QAAwB,EACxB,IAA6B;IAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,IAAI,qBAAqB,CAC7B,wBAAwB,iBAAiB,kBAAkB,SAAS,EAAE,EACtE,QAAQ,EACR,CAAC,iBAAiB,CAAC,CACpB,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,qBAAqB,CAC7B,wBAAwB,iBAAiB,uBAAuB,SAAS,EAAE,EAC3E,QAAQ,EACR,CAAC,iBAAiB,CAAC,CACpB,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,qBAAqB,CAC7B,+BAA+B,iBAAiB,KAAK,GAAG,MAAM,SAAS,EAAE,EACzE,GAAG,EACH,CAAC,iBAAiB,CAAC,CACpB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,qBAAqB,CAC7B,uBAAuB,iBAAiB,KAAK,GAAG,wCAAwC,SAAS,EAAE,EACnG,GAAG,EACH,CAAC,iBAAiB,CAAC,CACpB,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CACrB,MAAwB,EACxB,IAA6B;IAE7B,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS;QAC9B,iBAAiB;QACjB,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KAC9B,CAAC,CAAC;IACH,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACxE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;QAC9B,+EAA+E;QAC/E,MAAM,OAAO,GAAG,CAAC,iBAAiB,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,2EAA2E;IAC3E,6EAA6E;IAC7E,4EAA4E;IAC5E,uCAAuC;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACxE,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,2EAA2E;QAC3E,8EAA8E;QAC9E,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7E,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC1C,IAAI,KAAK,CAAC,QAAQ;gBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7C,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,eAAe,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YACxD,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,yBAAyB,KAAK,CAAC,IAAI,oBAAoB,CAAC,CAAC;YACnF,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACpE,OAAO,CAAC,IAAI,CACV,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,oBAAoB,KAAK,CAAC,SAAS,GAAG,CACrE,CAAC;YACF,SAAS;QACX,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IAC7B,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,4BAA4B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,QAAQ,CAAC,IAAI,CAAC,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,qBAAqB,CAC7B,0BAA0B,iBAAiB,KAAK,MAAM,CAAC,UAAU,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC5F,4EAA4E,EAC9E,MAAM,CAAC,UAAU,EACjB,SAAS,CACV,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAoC;IACvE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,OAAO,GAAG,SAAS,IAAI,WAAW,CAAC;IACzC,MAAM,GAAG,GAAG,EAAE,UAAU,EAAE,CAAC;IAC3B,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EAAE,0BAA0B,CAAC,QAAQ,CAAC;QACjD,UAAU,EAAE,yBAAyB,CAAC,QAAQ,CAAC;QAC/C,KAAK,CAAC,OAAO,CAAC,WAAmB,EAAE,MAAe;YAChD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;gBAC7C,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC5C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBACjD,MAAM,EAAE,IAAI,CACV,8CAA8C,MAAM,CAAC,UAAU,mBAAmB,MAAM,CAAC,aAAa,YAAY,OAAO,EAAE,CAC5H,CAAC;gBACF,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;oBAC9C,OAAO,EAAE,MAAM,CAAC,OAAO;iBACxB,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;oBACzC,MAAM,EAAE,IAAI,CACV,mDAAmD,GAAG,CAAC,UAAU,WAAW,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,OAAO,MAAM,GAAG,CAAC,OAAO,EAAE,CACvI,CAAC;gBACJ,CAAC;qBAAM,IAAI,GAAG,YAAY,iBAAiB,EAAE,CAAC;oBAC5C,wEAAwE;oBACxE,iEAAiE;oBACjE,MAAM,EAAE,IAAI,CACV,+CAA+C,GAAG,CAAC,UAAU,YAAY,OAAO,MAAM,GAAG,CAAC,OAAO,YAC/F,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,QAAQ,CAC/E,EAAE,CACH,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,MAAM,EAAE,KAAK,EAAE,CACb,+CAA+C,OAAO,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC/G,CAAC;gBACJ,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget spec types.
|
|
3
|
+
*
|
|
4
|
+
* A widget spec is the single source for one `widget_type`: the fields it
|
|
5
|
+
* accepts, the text that teaches the model about it, and the handler that
|
|
6
|
+
* builds the result. The `show_widget` tool descriptor (enum + description) is
|
|
7
|
+
* generated from the spec list, so the two cannot drift.
|
|
8
|
+
*/
|
|
9
|
+
/** Field value kinds a widget may declare. Extend as new widgets need them. */
|
|
10
|
+
export type WidgetFieldType = "string";
|
|
11
|
+
export interface WidgetFieldSpec {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly type: WidgetFieldType;
|
|
14
|
+
/** Enforced in `execute()`; the declared JSON schema keeps every field optional. */
|
|
15
|
+
readonly required: boolean;
|
|
16
|
+
/** One line, shown to the model in the generated tool description. */
|
|
17
|
+
readonly description: string;
|
|
18
|
+
/** Upper bound for string fields. */
|
|
19
|
+
readonly maxLength?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Rendered by the client. Always carries the three shared keys. */
|
|
22
|
+
export interface WidgetPayload {
|
|
23
|
+
readonly widget_type: string;
|
|
24
|
+
readonly widget_version: number;
|
|
25
|
+
readonly status: string;
|
|
26
|
+
readonly [key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface WidgetResult {
|
|
29
|
+
readonly payload: WidgetPayload;
|
|
30
|
+
/** Short line for the agent — what happened and what not to do next. */
|
|
31
|
+
readonly text: string;
|
|
32
|
+
}
|
|
33
|
+
/** One of the caller's strategies, as the backend reported it. */
|
|
34
|
+
export interface StrategyMatch {
|
|
35
|
+
readonly id: string;
|
|
36
|
+
/** The address exactly as the backend spells it, not as the model typed it. */
|
|
37
|
+
readonly walletAddress: string;
|
|
38
|
+
/** Lifecycle status, e.g. "ACTIVE", "PAUSED", "CLOSED". */
|
|
39
|
+
readonly status: string;
|
|
40
|
+
readonly name: string;
|
|
41
|
+
}
|
|
42
|
+
export interface StrategyLookup {
|
|
43
|
+
/**
|
|
44
|
+
* `null` means the backend answered and no strategy of the caller's holds that
|
|
45
|
+
* address. A backend that did not answer usably **throws** — the two must never
|
|
46
|
+
* collapse, or one timeout turns a good address into "not yours".
|
|
47
|
+
*/
|
|
48
|
+
findByWallet(wallet: string): Promise<StrategyMatch | null>;
|
|
49
|
+
}
|
|
50
|
+
/** Everything a handler may reach for beyond its own args. */
|
|
51
|
+
export interface WidgetContext {
|
|
52
|
+
/** Absent when the plugin registered the tool without a backend. */
|
|
53
|
+
readonly strategies?: StrategyLookup;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A field a widget always refuses but wants to diagnose itself, because another
|
|
57
|
+
* widget type declares it and the model can misroute it here.
|
|
58
|
+
*
|
|
59
|
+
* Matched by name only. No type or length check is applied, and the value
|
|
60
|
+
* reaches the handler exactly as it arrived: the widget's answer is "drop this
|
|
61
|
+
* field", which does not change with the value, and a type complaint would send
|
|
62
|
+
* the model back to correct a field it should be removing.
|
|
63
|
+
*/
|
|
64
|
+
export interface GuardedFieldSpec {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
/** Why it is refused. For readers of the spec; the model sees the widget's own error. */
|
|
67
|
+
readonly description: string;
|
|
68
|
+
}
|
|
69
|
+
export interface WidgetSpec {
|
|
70
|
+
readonly widgetType: string;
|
|
71
|
+
/** Bumped when the payload shape changes; the client falls back on unknown versions. */
|
|
72
|
+
readonly widgetVersion: number;
|
|
73
|
+
/** When to use this widget. Reproduced verbatim in the tool description. */
|
|
74
|
+
readonly description: string;
|
|
75
|
+
readonly fields: readonly WidgetFieldSpec[];
|
|
76
|
+
/** Never advertised in the description or the declared schema. */
|
|
77
|
+
readonly guardedFields?: readonly GuardedFieldSpec[];
|
|
78
|
+
/** Receives args validated against `fields`, plus any guarded field verbatim. */
|
|
79
|
+
readonly handler: (args: Readonly<Record<string, unknown>>, ctx: WidgetContext) => WidgetResult | Promise<WidgetResult>;
|
|
80
|
+
}
|
|
81
|
+
/** A spec plus its resolved on/off state from plugin config. */
|
|
82
|
+
export interface RegisteredWidget extends WidgetSpec {
|
|
83
|
+
readonly enabled: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface WidgetLogger {
|
|
86
|
+
info(...args: unknown[]): void;
|
|
87
|
+
warn(...args: unknown[]): void;
|
|
88
|
+
/** Unhandled failures only; every expected rejection is a WARN. */
|
|
89
|
+
error?(...args: unknown[]): void;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/widgets/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;CAC7D;AAED,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC;CACtC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wFAAwF;IACxF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrD,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,CAChB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACvC,GAAG,EAAE,aAAa,KACf,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC3C;AAED,gEAAgE;AAChE,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,mEAAmE;IACnE,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget spec types.
|
|
3
|
+
*
|
|
4
|
+
* A widget spec is the single source for one `widget_type`: the fields it
|
|
5
|
+
* accepts, the text that teaches the model about it, and the handler that
|
|
6
|
+
* builds the result. The `show_widget` tool descriptor (enum + description) is
|
|
7
|
+
* generated from the spec list, so the two cannot drift.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/widgets/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WidgetSpec } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* `fund_strategy_wallet` — a card whose button tops the strategy up from the
|
|
4
|
+
* user's own Senpi wallet. This is an internal transfer, not a deposit: a
|
|
5
|
+
* strategy wallet has no deposit address anyone may be shown, because USDC sent
|
|
6
|
+
* to it directly bypasses accounting and may be unrecoverable.
|
|
7
|
+
*
|
|
8
|
+
* The address is validated here rather than at the schema, because "is one of
|
|
9
|
+
* this user's active strategies" is not something a JSON schema can state. The
|
|
10
|
+
* payload carries the resolved strategy id: the top-up API keys on the id.
|
|
11
|
+
*/
|
|
12
|
+
export declare const fundStrategyWalletSpec: WidgetSpec;
|
|
13
|
+
//# sourceMappingURL=fund-strategy-wallet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fund-strategy-wallet.d.ts","sourceRoot":"","sources":["../../../src/widgets/widgets/fund-strategy-wallet.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgC,UAAU,EAAE,MAAM,aAAa,CAAC;AA4G5E;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,EAAE,UA0DpC,CAAC"}
|