@senpi/runtime 3.0.146 → 3.0.149
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/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +55 -0
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/cli/quant-command.d.ts +128 -0
- package/dist/cli/quant-command.d.ts.map +1 -0
- package/dist/cli/quant-command.js +345 -0
- package/dist/cli/quant-command.js.map +1 -0
- package/dist/cli/senpi-commands.d.ts +11 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +49 -4
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -1
- package/dist/quant/engine/quantdesk/__init__.py +19 -0
- package/dist/quant/engine/quantdesk/__main__.py +281 -0
- package/dist/quant/engine/quantdesk/_version.py +24 -0
- package/dist/quant/engine/quantdesk/benchmark.py +90 -0
- package/dist/quant/engine/quantdesk/book.py +197 -0
- package/dist/quant/engine/quantdesk/deep.py +197 -0
- package/dist/quant/engine/quantdesk/desk.py +878 -0
- package/dist/quant/engine/quantdesk/dsl.py +224 -0
- package/dist/quant/engine/quantdesk/followups.py +117 -0
- package/dist/quant/engine/quantdesk/hl_api.py +473 -0
- package/dist/quant/engine/quantdesk/market.py +290 -0
- package/dist/quant/engine/quantdesk/mcp_client.py +158 -0
- package/dist/quant/engine/quantdesk/metrics.py +409 -0
- package/dist/quant/engine/quantdesk/opportunities.py +88 -0
- package/dist/quant/engine/quantdesk/render.py +787 -0
- package/dist/quant/engine/quantdesk/roundtrips.py +124 -0
- package/dist/quant/engine/quantdesk/score.py +817 -0
- package/dist/quant/engine/quantdesk/senpi_history.py +160 -0
- package/dist/quant/engine/quantdesk/smart_money.py +354 -0
- package/dist/quant/engine/quantdesk/strategy_read.py +292 -0
- package/dist/quant/engine/quantdesk/taxonomy.py +55 -0
- package/dist/quant/engine/quantdesk/timing.py +164 -0
- package/dist/quant/engine/quantdesk/voice.py +42 -0
- package/dist/quant/engine-runner.d.ts +133 -0
- package/dist/quant/engine-runner.d.ts.map +1 -0
- package/dist/quant/engine-runner.js +306 -0
- package/dist/quant/engine-runner.js.map +1 -0
- package/dist/quant/job.d.ts +125 -0
- package/dist/quant/job.d.ts.map +1 -0
- package/dist/quant/job.js +184 -0
- package/dist/quant/job.js.map +1 -0
- package/dist/quant/register.d.ts +53 -0
- package/dist/quant/register.d.ts.map +1 -0
- package/dist/quant/register.js +253 -0
- package/dist/quant/register.js.map +1 -0
- package/dist/quant/registry.d.ts +105 -0
- package/dist/quant/registry.d.ts.map +1 -0
- package/dist/quant/registry.js +248 -0
- package/dist/quant/registry.js.map +1 -0
- package/dist/quant/types.d.ts +216 -0
- package/dist/quant/types.d.ts.map +1 -0
- package/dist/quant/types.js +121 -0
- package/dist/quant/types.js.map +1 -0
- package/dist/skills-manager/manifest.js +1 -1
- package/dist/skills-manager/skills-manifest.json +73 -16
- package/dist/widgets/index.d.ts +8 -1
- package/dist/widgets/index.d.ts.map +1 -1
- package/dist/widgets/index.js +1 -0
- package/dist/widgets/index.js.map +1 -1
- package/dist/widgets/registry.d.ts +8 -0
- package/dist/widgets/registry.d.ts.map +1 -1
- package/dist/widgets/registry.js +26 -1
- package/dist/widgets/registry.js.map +1 -1
- package/dist/widgets/tool.d.ts +14 -4
- package/dist/widgets/tool.d.ts.map +1 -1
- package/dist/widgets/tool.js +36 -23
- package/dist/widgets/tool.js.map +1 -1
- package/dist/widgets/turn-cap.d.ts +33 -0
- package/dist/widgets/turn-cap.d.ts.map +1 -0
- package/dist/widgets/turn-cap.js +119 -0
- package/dist/widgets/turn-cap.js.map +1 -0
- package/dist/widgets/types.d.ts +33 -5
- package/dist/widgets/types.d.ts.map +1 -1
- package/dist/widgets/types.js +4 -3
- package/dist/widgets/types.js.map +1 -1
- package/dist/widgets/widgets/fund-user-wallet.d.ts.map +1 -1
- package/dist/widgets/widgets/fund-user-wallet.js +1 -0
- package/dist/widgets/widgets/fund-user-wallet.js.map +1 -1
- package/dist/widgets/widgets/quant-desk-recommendations.d.ts +52 -0
- package/dist/widgets/widgets/quant-desk-recommendations.d.ts.map +1 -0
- package/dist/widgets/widgets/quant-desk-recommendations.js +278 -0
- package/dist/widgets/widgets/quant-desk-recommendations.js.map +1 -0
- package/openclaw.plugin.json +12 -2
- package/package.json +4 -3
|
@@ -2,23 +2,80 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"defaults": {
|
|
4
4
|
"repo": "https://github.com/Senpi-ai/senpi-skills",
|
|
5
|
-
"branch": "
|
|
5
|
+
"branch": "dev"
|
|
6
6
|
},
|
|
7
7
|
"skills": [
|
|
8
|
-
{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
8
|
+
{
|
|
9
|
+
"name": "senpi-trading-runtime",
|
|
10
|
+
"maxMajor": 4
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "senpi-strategy-author",
|
|
14
|
+
"maxMajor": 3,
|
|
15
|
+
"preserve": [
|
|
16
|
+
"strategies",
|
|
17
|
+
"scripts/strategies"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "senpi-strategy-discover",
|
|
22
|
+
"maxMajor": 2,
|
|
23
|
+
"preserve": [
|
|
24
|
+
"strategies",
|
|
25
|
+
"scripts/strategies"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "senpi-strategy-ops",
|
|
30
|
+
"maxMajor": 3,
|
|
31
|
+
"preserve": [
|
|
32
|
+
"strategies",
|
|
33
|
+
"scripts/strategies"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "senpi-portfolio",
|
|
38
|
+
"maxMajor": 1
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "senpi-improve-trades",
|
|
42
|
+
"maxMajor": 1
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "senpi-market-pulse",
|
|
46
|
+
"maxMajor": 1
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "senpi-smart-money",
|
|
50
|
+
"maxMajor": 1
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "quant-desk",
|
|
54
|
+
"maxMajor": 2
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "senpi-signals",
|
|
58
|
+
"maxMajor": 2
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "senpi-trader-research",
|
|
62
|
+
"maxMajor": 1
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "senpi-trade",
|
|
66
|
+
"maxMajor": 1
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "senpi-account-status",
|
|
70
|
+
"maxMajor": 1
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "senpi-why",
|
|
74
|
+
"maxMajor": 1
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "senpi-deposit-withdraw-transfer",
|
|
78
|
+
"maxMajor": 1
|
|
79
|
+
}
|
|
23
80
|
]
|
|
24
81
|
}
|
package/dist/widgets/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { WidgetLogger } from "./types.js";
|
|
2
|
+
import type { QuantRunReader } from "../quant/types.js";
|
|
2
3
|
export { WIDGET_SPECS, buildWidgetRegistry } from "./registry.js";
|
|
3
4
|
export type { WidgetRegistry, WidgetsConfig } from "./registry.js";
|
|
4
5
|
export { WidgetValidationError } from "./errors.js";
|
|
5
6
|
export { SHOW_WIDGET_TOOL_NAME, buildShowWidgetDescription, buildShowWidgetParameters, createShowWidgetTool, } from "./tool.js";
|
|
6
7
|
export type { ShowWidgetTool, ShowWidgetToolResult } from "./tool.js";
|
|
7
|
-
export type { RegisteredWidget, WidgetPayload, WidgetResult, WidgetSpec } from "./types.js";
|
|
8
|
+
export type { RegisteredWidget, WidgetHandlerContext, WidgetPayload, WidgetResult, WidgetSpec } from "./types.js";
|
|
8
9
|
/** The slice of the OpenClaw plugin API this module needs. */
|
|
9
10
|
export interface WidgetToolPluginApi {
|
|
10
11
|
registerTool(tool: (ctx: {
|
|
@@ -19,6 +20,12 @@ export interface RegisterShowWidgetToolOptions {
|
|
|
19
20
|
/** The plugin config `widgets` block: `{ "<widget_type>": boolean }`. */
|
|
20
21
|
widgetsConfig?: unknown;
|
|
21
22
|
logger?: WidgetLogger;
|
|
23
|
+
/**
|
|
24
|
+
* The quant desk's run registry (read side), for `quant_desk_recommendations`. Absent when the
|
|
25
|
+
* desk failed to build: the tool still registers, and that type answers with a teaching error
|
|
26
|
+
* telling the model to relay the desk's next steps as text.
|
|
27
|
+
*/
|
|
28
|
+
quantRuns?: QuantRunReader;
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* Register `show_widget` as a plugin tool factory.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/widgets/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/widgets/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElH,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IAClC,YAAY,CACV,IAAI,EAAE,CAAC,GAAG,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,EAC9C,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAC9C,IAAI,CAAC;CACT;AAED,MAAM,WAAW,6BAA6B;IAC5C,GAAG,EAAE,mBAAmB,CAAC;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,OAAO,CA2BtF"}
|
package/dist/widgets/index.js
CHANGED
|
@@ -26,6 +26,7 @@ export function registerShowWidgetTool(options) {
|
|
|
26
26
|
registry,
|
|
27
27
|
logger,
|
|
28
28
|
sessionId: ctx?.sessionId,
|
|
29
|
+
handlerContext: options.quantRuns ? { quantRuns: options.quantRuns } : {},
|
|
29
30
|
}), { names: [SHOW_WIDGET_TOOL_NAME] });
|
|
30
31
|
logger?.info(`[senpi-widgets] ${SHOW_WIDGET_TOOL_NAME} registered | widget_types=${enabled.join(",")}`);
|
|
31
32
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAyBnB;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAsC;IAC3E,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAChC,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC;IACxC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,EAAE,IAAI,CACV,6FAA6F,CAC9F,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACpC,GAAG,CAAC,YAAY,CACd,CAAC,GAA2B,EAAE,EAAE,CAC9B,oBAAoB,CAAC;QACnB,QAAQ;QACR,MAAM;QACN,SAAS,EAAE,GAAG,EAAE,SAAS;QACzB,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE;KAC1E,CAAC,EACJ,EAAE,KAAK,EAAE,CAAC,qBAAqB,CAAC,EAAE,CACnC,CAAC;IACF,MAAM,EAAE,IAAI,CACV,mBAAmB,qBAAqB,8BAA8B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAC1F,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -20,5 +20,13 @@ export interface WidgetRegistry {
|
|
|
20
20
|
get(widgetType: string): RegisteredWidget | undefined;
|
|
21
21
|
enabledTypes(): string[];
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the spec list against plugin config.
|
|
25
|
+
*
|
|
26
|
+
* @param config The plugin config `widgets` block (see {@link WidgetsConfig}).
|
|
27
|
+
* @param specs The spec list; defaults to {@link WIDGET_SPECS}. Injected by tests.
|
|
28
|
+
* @throws Error when two specs share a `widget_type`, when two specs declare the same extra
|
|
29
|
+
* parameter name, or when a spec declares `widget_type` as an extra parameter.
|
|
30
|
+
*/
|
|
23
31
|
export declare function buildWidgetRegistry(config?: unknown, specs?: readonly WidgetSpec[]): WidgetRegistry;
|
|
24
32
|
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/widgets/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/widgets/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI/D;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAuD,CAAC;AAEtG;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,GAAG,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACnC,yEAAyE;IACzE,OAAO,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACvC,+CAA+C;IAC/C,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACtD,YAAY,IAAI,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,CAAC,EAAE,OAAO,EAChB,KAAK,GAAE,SAAS,UAAU,EAAiB,GAC1C,cAAc,CAyChB"}
|
package/dist/widgets/registry.js
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { fundUserWalletSpec } from "./widgets/fund-user-wallet.js";
|
|
2
|
+
import { quantDeskRecommendationsSpec } from "./widgets/quant-desk-recommendations.js";
|
|
2
3
|
/**
|
|
3
4
|
* Every widget type this plugin can render. Adding one here adds it to the
|
|
4
5
|
* `show_widget` enum, to the generated tool description, and to the config
|
|
5
6
|
* gate — the tool name and the manifest are untouched.
|
|
6
7
|
*/
|
|
7
|
-
export const WIDGET_SPECS = [fundUserWalletSpec];
|
|
8
|
+
export const WIDGET_SPECS = [fundUserWalletSpec, quantDeskRecommendationsSpec];
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the spec list against plugin config.
|
|
11
|
+
*
|
|
12
|
+
* @param config The plugin config `widgets` block (see {@link WidgetsConfig}).
|
|
13
|
+
* @param specs The spec list; defaults to {@link WIDGET_SPECS}. Injected by tests.
|
|
14
|
+
* @throws Error when two specs share a `widget_type`, when two specs declare the same extra
|
|
15
|
+
* parameter name, or when a spec declares `widget_type` as an extra parameter.
|
|
16
|
+
*/
|
|
8
17
|
export function buildWidgetRegistry(config, specs = WIDGET_SPECS) {
|
|
18
|
+
// Parameters are one flat object shared by every type, so each extra name has exactly one owner.
|
|
19
|
+
// Checked over ALL specs, not just enabled ones: a clash hidden while one type is disabled would
|
|
20
|
+
// surface the day config flips it on.
|
|
21
|
+
const paramOwner = new Map();
|
|
22
|
+
for (const spec of specs) {
|
|
23
|
+
for (const key of Object.keys(spec.extraParameters ?? {})) {
|
|
24
|
+
if (key === "widget_type") {
|
|
25
|
+
throw new Error(`show_widget: ${spec.widgetType} declares "widget_type" as an extra parameter; it is the shared discriminator`);
|
|
26
|
+
}
|
|
27
|
+
const owner = paramOwner.get(key);
|
|
28
|
+
if (owner) {
|
|
29
|
+
throw new Error(`show_widget: parameter "${key}" is declared by both ${owner} and ${spec.widgetType}; each name has one owner`);
|
|
30
|
+
}
|
|
31
|
+
paramOwner.set(key, spec.widgetType);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
9
34
|
const gate = config && typeof config === "object" && !Array.isArray(config)
|
|
10
35
|
? config
|
|
11
36
|
: undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/widgets/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/widgets/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAC;AAEvF;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAA0B,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;AAmBtG;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAgB,EAChB,QAA+B,YAAY;IAE3C,iGAAiG;IACjG,iGAAiG;IACjG,sCAAsC;IACtC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,EAAE,CAAC;YAC1D,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,UAAU,+EAA+E,CAAC,CAAC;YAClI,CAAC;YACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,yBAAyB,KAAK,QAAQ,IAAI,CAAC,UAAU,2BAA2B,CAAC,CAAC;YAClI,CAAC;YACD,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GACR,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAC5D,CAAC,CAAE,MAAkC;QACrC,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,OAAO,GAAuB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,GAAG,IAAI;QACP,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,KAAK;KAC3C,CAAC,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,sEAAsE;QACtE,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,sDAAsD,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;QAC9F,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACjD,OAAO;QACL,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO;QAClB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO;QACtB,GAAG,EAAE,CAAC,UAAkB,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;QACnD,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;KACrD,CAAC;AACJ,CAAC"}
|
package/dist/widgets/tool.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WidgetRegistry } from "./registry.js";
|
|
2
|
-
import type { WidgetLogger, WidgetPayload } from "./types.js";
|
|
2
|
+
import type { WidgetHandlerContext, WidgetLogger, WidgetPayload } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* The plugin's single agent tool. The name is static — new widget types are
|
|
5
5
|
* new `widget_type` enum values, never new tools — so it is declared once in
|
|
@@ -24,25 +24,35 @@ export interface CreateShowWidgetToolOptions {
|
|
|
24
24
|
logger?: WidgetLogger;
|
|
25
25
|
/** Ephemeral session id from the plugin tool factory context; logged only. */
|
|
26
26
|
sessionId?: string;
|
|
27
|
+
/** Collaborators handed to every handler (e.g. the quant run reader). Defaults to `{}`. */
|
|
28
|
+
handlerContext?: WidgetHandlerContext;
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* Tool description, generated from the enabled specs. Nothing else teaches the
|
|
30
32
|
* model about these cards, so this text carries the condition that should make
|
|
31
33
|
* it call the tool, not just the shape of the call.
|
|
34
|
+
*
|
|
35
|
+
* The shared header claims nothing that is true of only one type: how a card is
|
|
36
|
+
* acted on (`card:`) and which fields it takes (`fields:`) are stated per type,
|
|
37
|
+
* because not every card has a button and not every type takes widget_type only.
|
|
32
38
|
*/
|
|
33
39
|
export declare function buildShowWidgetDescription(registry: WidgetRegistry): string;
|
|
34
40
|
/**
|
|
35
41
|
* Declared JSON schema: one flat object, no unions at any depth.
|
|
36
42
|
*
|
|
37
43
|
* OpenClaw flattens top-level unions, Gemini flattens nested ones, and OpenAI
|
|
38
|
-
* strict mode rejects a schema containing one anywhere.
|
|
39
|
-
*
|
|
44
|
+
* strict mode rejects a schema containing one anywhere. So there is no per-type
|
|
45
|
+
* branch: `widget_type` is the one required property, and every enabled spec's
|
|
46
|
+
* `extraParameters` are merged in beside it as optional properties. Each such
|
|
47
|
+
* property belongs to one type (the registry refuses a shared name) and is
|
|
48
|
+
* validated by that type's handler; `rejectExtraFields` refuses one sent with
|
|
49
|
+
* any other type.
|
|
40
50
|
*/
|
|
41
51
|
export declare function buildShowWidgetParameters(registry: WidgetRegistry): Record<string, unknown>;
|
|
42
52
|
/**
|
|
43
53
|
* Build the `show_widget` tool for one agent turn. `sessionId` comes from the
|
|
44
54
|
* plugin tool factory context and is used for logging only — nothing about the
|
|
45
|
-
* result depends on it.
|
|
55
|
+
* result depends on it. `handlerContext` is passed to every handler as-is.
|
|
46
56
|
*/
|
|
47
57
|
export declare function createShowWidgetTool(options: CreateShowWidgetToolOptions): ShowWidgetTool;
|
|
48
58
|
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -1 +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,EAAoB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
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,EAAoB,oBAAoB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtG;;;;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,2FAA2F;IAC3F,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAwB3E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAc3F;AAuED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,cAAc,CAmCzF"}
|
package/dist/widgets/tool.js
CHANGED
|
@@ -10,6 +10,10 @@ const WIDGET_TYPE_PARAM = "widget_type";
|
|
|
10
10
|
* Tool description, generated from the enabled specs. Nothing else teaches the
|
|
11
11
|
* model about these cards, so this text carries the condition that should make
|
|
12
12
|
* it call the tool, not just the shape of the call.
|
|
13
|
+
*
|
|
14
|
+
* The shared header claims nothing that is true of only one type: how a card is
|
|
15
|
+
* acted on (`card:`) and which fields it takes (`fields:`) are stated per type,
|
|
16
|
+
* because not every card has a button and not every type takes widget_type only.
|
|
13
17
|
*/
|
|
14
18
|
export function buildShowWidgetDescription(registry) {
|
|
15
19
|
const widgets = registry.enabled();
|
|
@@ -17,16 +21,18 @@ export function buildShowWidgetDescription(registry) {
|
|
|
17
21
|
return "Render a Senpi UI card in the user's chat.\n\nNo widget types are enabled on this deployment.";
|
|
18
22
|
}
|
|
19
23
|
const lines = [
|
|
20
|
-
"Render a Senpi UI card in the user's chat.
|
|
24
|
+
"Render a Senpi UI card in the user's chat.",
|
|
21
25
|
"",
|
|
22
26
|
"Call it yourself as part of your answer — the user does not have to ask for a card. Each type below states when to call it; the examples illustrate that rule, they are not a checklist to match.",
|
|
23
27
|
"",
|
|
24
|
-
`Pass ${WIDGET_TYPE_PARAM} and nothing else.`,
|
|
28
|
+
`Pass ${WIDGET_TYPE_PARAM} plus the fields listed for that type, and nothing else.`,
|
|
25
29
|
"",
|
|
26
30
|
"Widget types:",
|
|
27
31
|
];
|
|
28
32
|
for (const widget of widgets) {
|
|
29
33
|
lines.push(`- ${widget.widgetType}: ${widget.description}`);
|
|
34
|
+
lines.push(` card: ${widget.affordance}`);
|
|
35
|
+
lines.push(` fields: ${widget.fields ?? `none — ${WIDGET_TYPE_PARAM} only`}`);
|
|
30
36
|
lines.push(` when: ${widget.callWhen}`);
|
|
31
37
|
lines.push(` not this card: ${widget.notFor}`);
|
|
32
38
|
lines.push(" for example:");
|
|
@@ -38,26 +44,28 @@ export function buildShowWidgetDescription(registry) {
|
|
|
38
44
|
* Declared JSON schema: one flat object, no unions at any depth.
|
|
39
45
|
*
|
|
40
46
|
* OpenClaw flattens top-level unions, Gemini flattens nested ones, and OpenAI
|
|
41
|
-
* strict mode rejects a schema containing one anywhere.
|
|
42
|
-
*
|
|
47
|
+
* strict mode rejects a schema containing one anywhere. So there is no per-type
|
|
48
|
+
* branch: `widget_type` is the one required property, and every enabled spec's
|
|
49
|
+
* `extraParameters` are merged in beside it as optional properties. Each such
|
|
50
|
+
* property belongs to one type (the registry refuses a shared name) and is
|
|
51
|
+
* validated by that type's handler; `rejectExtraFields` refuses one sent with
|
|
52
|
+
* any other type.
|
|
43
53
|
*/
|
|
44
54
|
export function buildShowWidgetParameters(registry) {
|
|
45
55
|
const widgets = registry.enabled();
|
|
46
56
|
if (widgets.length === 0) {
|
|
47
57
|
throw new Error("show_widget has no enabled widget types; do not register the tool");
|
|
48
58
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
enum: widgets.map((w) => w.widgetType),
|
|
55
|
-
description: "Which card to render.",
|
|
56
|
-
},
|
|
59
|
+
const properties = {
|
|
60
|
+
[WIDGET_TYPE_PARAM]: {
|
|
61
|
+
type: "string",
|
|
62
|
+
enum: widgets.map((w) => w.widgetType),
|
|
63
|
+
description: "Which card to render.",
|
|
57
64
|
},
|
|
58
|
-
required: [WIDGET_TYPE_PARAM],
|
|
59
|
-
additionalProperties: false,
|
|
60
65
|
};
|
|
66
|
+
for (const widget of widgets)
|
|
67
|
+
Object.assign(properties, widget.extraParameters ?? {});
|
|
68
|
+
return { type: "object", properties, required: [WIDGET_TYPE_PARAM], additionalProperties: false };
|
|
61
69
|
}
|
|
62
70
|
function availableTypesSentence(registry) {
|
|
63
71
|
const types = registry.enabledTypes();
|
|
@@ -90,24 +98,28 @@ function resolveWidget(registry, args) {
|
|
|
90
98
|
return widget;
|
|
91
99
|
}
|
|
92
100
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
101
|
+
* A type takes `widget_type` plus its own `extraParameters`, so any other key is
|
|
102
|
+
* a field the model invented — or one that belongs to another type, which the
|
|
103
|
+
* merged schema cannot refuse on its own. Every one is named in a single
|
|
104
|
+
* message, with the fields this type does take: the correction is the same for
|
|
105
|
+
* all of them, and the model can apply it in one retry.
|
|
96
106
|
*/
|
|
97
107
|
function rejectExtraFields(widget, args) {
|
|
98
|
-
const
|
|
108
|
+
const allowed = [WIDGET_TYPE_PARAM, ...Object.keys(widget.extraParameters ?? {})];
|
|
109
|
+
const unexpected = Object.keys(args).filter((key) => !allowed.includes(key));
|
|
99
110
|
if (unexpected.length === 0)
|
|
100
111
|
return;
|
|
112
|
+
const takes = allowed.length === 1 ? `${WIDGET_TYPE_PARAM} only` : `${allowed.join(", ")} for ${widget.widgetType}`;
|
|
101
113
|
throw new WidgetValidationError(`show_widget failed for ${WIDGET_TYPE_PARAM} "${widget.widgetType}": unexpected fields: ` +
|
|
102
|
-
`${unexpected.join(", ")}. show_widget takes ${
|
|
114
|
+
`${unexpected.join(", ")}. show_widget takes ${takes} — drop them and call it again.`, widget.widgetType, unexpected);
|
|
103
115
|
}
|
|
104
116
|
/**
|
|
105
117
|
* Build the `show_widget` tool for one agent turn. `sessionId` comes from the
|
|
106
118
|
* plugin tool factory context and is used for logging only — nothing about the
|
|
107
|
-
* result depends on it.
|
|
119
|
+
* result depends on it. `handlerContext` is passed to every handler as-is.
|
|
108
120
|
*/
|
|
109
121
|
export function createShowWidgetTool(options) {
|
|
110
|
-
const { registry, logger, sessionId } = options;
|
|
122
|
+
const { registry, logger, sessionId, handlerContext } = options;
|
|
111
123
|
const session = sessionId ?? "(unknown)";
|
|
112
124
|
return {
|
|
113
125
|
name: SHOW_WIDGET_TOOL_NAME,
|
|
@@ -118,8 +130,9 @@ export function createShowWidgetTool(options) {
|
|
|
118
130
|
const args = toArgs(params);
|
|
119
131
|
const widget = resolveWidget(registry, args);
|
|
120
132
|
rejectExtraFields(widget, args);
|
|
121
|
-
const result = await widget.handler();
|
|
122
|
-
logger?.info(`[senpi-widgets] widget shown | widget_type=${widget.widgetType} widget_version=${widget.widgetVersion}
|
|
133
|
+
const result = await widget.handler(args, handlerContext ?? {});
|
|
134
|
+
logger?.info(`[senpi-widgets] widget shown | widget_type=${widget.widgetType} widget_version=${widget.widgetVersion}` +
|
|
135
|
+
`${typeof args.run_id === "string" ? ` run_id=${args.run_id}` : ""} session=${session}`);
|
|
123
136
|
return {
|
|
124
137
|
content: [{ type: "text", text: result.text }],
|
|
125
138
|
details: result.payload,
|
package/dist/widgets/tool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/widgets/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD,MAAM,iBAAiB,GAAG,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/widgets/tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** How long a run's entry is kept after its last touch. */
|
|
2
|
+
export declare const TURN_CAP_TTL_MS: number;
|
|
3
|
+
/** The reason the model reads on a blocked call. */
|
|
4
|
+
export declare const TURN_CAP_BLOCK_REASON = "[E_WIDGET_TURN_CAP] recommendation cards were already shown in this answer; fold anything else into text.";
|
|
5
|
+
/** The cap's two hook bodies, exposed for tests. */
|
|
6
|
+
export interface QuantWidgetTurnCap {
|
|
7
|
+
beforeToolCall(event: unknown, ctx?: unknown): {
|
|
8
|
+
block: true;
|
|
9
|
+
blockReason: string;
|
|
10
|
+
} | undefined;
|
|
11
|
+
afterToolCall(event: unknown, ctx?: unknown): void;
|
|
12
|
+
trackedRuns(): number;
|
|
13
|
+
}
|
|
14
|
+
/** Build the cap's state machine. `now` is injected so expiry is testable. */
|
|
15
|
+
export declare function createQuantWidgetTurnCap(opts: {
|
|
16
|
+
now(): number;
|
|
17
|
+
ttlMs?: number;
|
|
18
|
+
}): QuantWidgetTurnCap;
|
|
19
|
+
/** The slice of the plugin API the cap needs. */
|
|
20
|
+
export interface TurnCapPluginApi {
|
|
21
|
+
on(hookName: string, handler: (...args: unknown[]) => unknown, opts?: {
|
|
22
|
+
priority?: number;
|
|
23
|
+
}): void;
|
|
24
|
+
}
|
|
25
|
+
/** Register the cap on `before_tool_call` / `after_tool_call`. Fails open on any internal error. */
|
|
26
|
+
export declare function registerQuantWidgetTurnCap(api: TurnCapPluginApi, opts: {
|
|
27
|
+
now(): number;
|
|
28
|
+
logger?: {
|
|
29
|
+
info(...a: unknown[]): void;
|
|
30
|
+
warn(...a: unknown[]): void;
|
|
31
|
+
};
|
|
32
|
+
}): QuantWidgetTurnCap;
|
|
33
|
+
//# sourceMappingURL=turn-cap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-cap.d.ts","sourceRoot":"","sources":["../../src/widgets/turn-cap.ts"],"names":[],"mappings":"AA2BA,2DAA2D;AAC3D,eAAO,MAAM,eAAe,QAAc,CAAC;AAC3C,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,8GAC2E,CAAC;AAe9G,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAChG,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACnD,WAAW,IAAI,MAAM,CAAC;CACvB;AAmBD,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAAE,GAAG,IAAI,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,kBAAkB,CAwCpG;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACpG;AAED,oGAAoG;AACpG,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE;IAAE,GAAG,IAAI,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,GAC7F,kBAAkB,CAoBpB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run cap on `quant_desk_recommendations` (spec D8).
|
|
3
|
+
*
|
|
4
|
+
* `maxItems` bounds one call; a model that wants seven cards parallelises instead (field finding
|
|
5
|
+
* 4). Tool factories carry no run id, hooks do, so the cap lives in `before_tool_call`: while a
|
|
6
|
+
* quant card call is in flight or has succeeded in an agent run, any further one in that run is
|
|
7
|
+
* blocked with a teaching reason. `after_tool_call` makes the cap exact rather than a counter: a
|
|
8
|
+
* FAILED call (a mistyped run id) frees the slot so the corrected retry renders, and the after-event
|
|
9
|
+
* of a call the cap itself blocked is ignored (matched by toolCallId). Entries expire after 10 min.
|
|
10
|
+
* No run id from the host → fail open. The hooks never throw into the tool pipeline.
|
|
11
|
+
*
|
|
12
|
+
* Host shapes (OpenClaw `PluginHookBeforeToolCallEvent` / `PluginHookAfterToolCallEvent` /
|
|
13
|
+
* `PluginHookToolContext`): both the event and the ctx carry optional `runId` and `toolCallId`, so
|
|
14
|
+
* the event is read first and the ctx is the fallback. The event carries `toolName` and `params`;
|
|
15
|
+
* the after-event adds `result` and `error` (a string, set when the tool threw or returned an error
|
|
16
|
+
* result — `show_widget` throws on every refusal). A `{ block, blockReason }` return from
|
|
17
|
+
* `before_tool_call` stops the call and hands `blockReason` to the model as the tool error.
|
|
18
|
+
*
|
|
19
|
+
* Parallel calls in one assistant message (the fleet model's way past `maxItems`) all reach
|
|
20
|
+
* `before_tool_call` before any `after_tool_call`; the in-flight `pendingCallId` is what blocks
|
|
21
|
+
* the second one in that window.
|
|
22
|
+
*/
|
|
23
|
+
import { SHOW_WIDGET_TOOL_NAME } from "./tool.js";
|
|
24
|
+
const QUANT_WIDGET_TYPE = "quant_desk_recommendations";
|
|
25
|
+
/** The call id recorded when the host supplies none; it cannot identify a call on its own. */
|
|
26
|
+
const UNKNOWN_CALL_ID = "(unknown)";
|
|
27
|
+
/** How long a run's entry is kept after its last touch. */
|
|
28
|
+
export const TURN_CAP_TTL_MS = 10 * 60_000;
|
|
29
|
+
/** The reason the model reads on a blocked call. */
|
|
30
|
+
export const TURN_CAP_BLOCK_REASON = "[E_WIDGET_TURN_CAP] recommendation cards were already shown in this answer; fold anything else into text.";
|
|
31
|
+
function str(x) {
|
|
32
|
+
return typeof x === "string" && x.length > 0 ? x : undefined;
|
|
33
|
+
}
|
|
34
|
+
function read(event, ctx) {
|
|
35
|
+
const ev = (event && typeof event === "object" ? event : {});
|
|
36
|
+
const c = (ctx && typeof ctx === "object" ? ctx : {});
|
|
37
|
+
const params = ev.params && typeof ev.params === "object" ? ev.params : undefined;
|
|
38
|
+
const runId = str(ev.runId) ?? str(c.runId);
|
|
39
|
+
return {
|
|
40
|
+
ev,
|
|
41
|
+
...(runId ? { runId } : {}),
|
|
42
|
+
callId: str(ev.toolCallId) ?? str(c.toolCallId) ?? UNKNOWN_CALL_ID,
|
|
43
|
+
isQuant: ev.toolName === SHOW_WIDGET_TOOL_NAME && params?.widget_type === QUANT_WIDGET_TYPE,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Build the cap's state machine. `now` is injected so expiry is testable. */
|
|
47
|
+
export function createQuantWidgetTurnCap(opts) {
|
|
48
|
+
const ttl = opts.ttlMs ?? TURN_CAP_TTL_MS;
|
|
49
|
+
const runs = new Map();
|
|
50
|
+
const sweep = () => {
|
|
51
|
+
const cutoff = opts.now() - ttl;
|
|
52
|
+
for (const [k, e] of runs)
|
|
53
|
+
if (e.touchedAt <= cutoff)
|
|
54
|
+
runs.delete(k);
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
beforeToolCall(event, ctx) {
|
|
58
|
+
const { runId, callId, isQuant } = read(event, ctx);
|
|
59
|
+
if (!isQuant || !runId)
|
|
60
|
+
return undefined;
|
|
61
|
+
sweep();
|
|
62
|
+
const entry = runs.get(runId);
|
|
63
|
+
if (entry && (entry.shown || entry.pendingCallId !== undefined)) {
|
|
64
|
+
entry.touchedAt = opts.now();
|
|
65
|
+
return { block: true, blockReason: TURN_CAP_BLOCK_REASON };
|
|
66
|
+
}
|
|
67
|
+
runs.set(runId, { shown: false, pendingCallId: callId, touchedAt: opts.now() });
|
|
68
|
+
return undefined;
|
|
69
|
+
},
|
|
70
|
+
afterToolCall(event, ctx) {
|
|
71
|
+
// No `isQuant` gate here: only a quant call can have set `pendingCallId`, so the toolCallId
|
|
72
|
+
// match is the identity. Re-deriving it from `params` would leave the entry pending forever
|
|
73
|
+
// when a host's after-event omits or empties them. The one exception is a call the host gave
|
|
74
|
+
// no id: then "(unknown)" matches any id-less event, so the tool itself must be the quant one.
|
|
75
|
+
const { ev, runId, callId, isQuant } = read(event, ctx);
|
|
76
|
+
if (!runId)
|
|
77
|
+
return;
|
|
78
|
+
const entry = runs.get(runId);
|
|
79
|
+
if (!entry || entry.pendingCallId !== callId)
|
|
80
|
+
return;
|
|
81
|
+
if (callId === UNKNOWN_CALL_ID && !isQuant)
|
|
82
|
+
return;
|
|
83
|
+
if (ev.error !== undefined && ev.error !== null && ev.error !== "") {
|
|
84
|
+
runs.delete(runId);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
entry.shown = true;
|
|
88
|
+
entry.pendingCallId = undefined;
|
|
89
|
+
entry.touchedAt = opts.now();
|
|
90
|
+
},
|
|
91
|
+
trackedRuns: () => runs.size,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Register the cap on `before_tool_call` / `after_tool_call`. Fails open on any internal error. */
|
|
95
|
+
export function registerQuantWidgetTurnCap(api, opts) {
|
|
96
|
+
const cap = createQuantWidgetTurnCap(opts);
|
|
97
|
+
api.on("before_tool_call", (event, ctx) => {
|
|
98
|
+
try {
|
|
99
|
+
const verdict = cap.beforeToolCall(event, ctx);
|
|
100
|
+
if (verdict)
|
|
101
|
+
opts.logger?.info(`[senpi-widgets] turn cap blocked a second ${QUANT_WIDGET_TYPE} call`);
|
|
102
|
+
return verdict;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
opts.logger?.warn(`[senpi-widgets] turn cap failed open: ${err instanceof Error ? err.message : String(err)}`);
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
api.on("after_tool_call", (event, ctx) => {
|
|
110
|
+
try {
|
|
111
|
+
cap.afterToolCall(event, ctx);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// the cap never breaks the tool pipeline
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return cap;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=turn-cap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-cap.js","sourceRoot":"","sources":["../../src/widgets/turn-cap.ts"],"names":[],"mappings":"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"}
|