ocsmarttools 0.1.4 → 0.1.6
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 +19 -0
- package/README.md +32 -21
- package/openclaw.plugin.json +2 -0
- package/package.json +1 -1
- package/src/commands/chat.ts +16 -0
- package/src/commands/cli.ts +19 -0
- package/src/commands/operations.ts +78 -25
- package/src/lib/bootstrap.ts +11 -1
- package/src/lib/plugin-config.ts +9 -4
- package/src/lib/routing-guide.ts +66 -20
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `ocsmarttools` are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.1.6] - 2026-02-22
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Rewrote help output in simpler plain-language wording for easier command discovery.
|
|
9
|
+
- Simplified README command descriptions to match the help command style.
|
|
10
|
+
|
|
11
|
+
## [0.1.5] - 2026-02-22
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Plugin-native strict routing mode (`strictRouting`) for fully self-managed routing behavior.
|
|
15
|
+
- New strict commands in chat and CLI:
|
|
16
|
+
- `/ocsmarttools strict <on|off|status>`
|
|
17
|
+
- `openclaw ocsmarttools strict <on|off>`
|
|
18
|
+
- Strict/non-strict auto-managed routing policy variants in `AGENTS.md`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- `strictRouting=true` now forces `autoInjectRoutingGuide=true`.
|
|
22
|
+
- Routing sync now applies the strict policy variant automatically when strict mode is enabled.
|
|
23
|
+
|
|
5
24
|
## [0.1.4] - 2026-02-22
|
|
6
25
|
|
|
7
26
|
### Changed
|
package/README.md
CHANGED
|
@@ -48,7 +48,13 @@ openclaw gateway restart
|
|
|
48
48
|
1. Install + enable + restart.
|
|
49
49
|
2. Done. The plugin auto-bootstraps and starts working in background.
|
|
50
50
|
3. It also auto-manages an OCSmartTools routing block in `AGENTS.md` (unless disabled).
|
|
51
|
-
4.
|
|
51
|
+
4. Enable strict plugin-managed routing (optional):
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
/ocsmarttools strict on
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
5. Optional check:
|
|
52
58
|
|
|
53
59
|
```text
|
|
54
60
|
/ocsmarttools status
|
|
@@ -64,35 +70,37 @@ Model note:
|
|
|
64
70
|
|
|
65
71
|
| Command | What it does |
|
|
66
72
|
|---|---|
|
|
67
|
-
| `/ocsmarttools
|
|
68
|
-
| `/ocsmarttools
|
|
69
|
-
| `/ocsmarttools status
|
|
70
|
-
| `/ocsmarttools
|
|
71
|
-
| `/ocsmarttools stats
|
|
72
|
-
| `/ocsmarttools
|
|
73
|
+
| `/ocsmarttools help` | Shows all commands in plain language |
|
|
74
|
+
| `/ocsmarttools status` | Shows mode and safety settings |
|
|
75
|
+
| `/ocsmarttools strict <on\|off\|status>` | Turns strict routing on/off or checks status |
|
|
76
|
+
| `/ocsmarttools sync` | Rewrites the managed routing block in `AGENTS.md` |
|
|
77
|
+
| `/ocsmarttools stats` | Shows calls, errors, timeouts, latency, and estimated savings |
|
|
78
|
+
| `/ocsmarttools stats reset` | Resets plugin stats |
|
|
79
|
+
| `/ocsmarttools setup [safe\|standard]` | Applies recommended defaults |
|
|
73
80
|
| `/ocsmarttools mode <safe\|standard>` | Changes mode only |
|
|
74
|
-
| `/ocsmarttools
|
|
75
|
-
| `/ocsmarttools config` | Shows effective plugin config |
|
|
81
|
+
| `/ocsmarttools config` | Shows plugin config |
|
|
76
82
|
| `/ocsmarttools config keys` | Lists editable config keys |
|
|
77
|
-
| `/ocsmarttools config set <key> <value>` |
|
|
78
|
-
| `/ocsmarttools config reset [key]` | Resets one key
|
|
83
|
+
| `/ocsmarttools config set <key> <value>` | Changes one config value |
|
|
84
|
+
| `/ocsmarttools config reset [key]` | Resets one key or all keys |
|
|
85
|
+
| `/ocsmarttools version` | Shows installed plugin version |
|
|
79
86
|
|
|
80
87
|
### CLI Commands
|
|
81
88
|
|
|
82
89
|
| Command | What it does |
|
|
83
90
|
|---|---|
|
|
84
|
-
| `openclaw ocsmarttools
|
|
85
|
-
| `openclaw ocsmarttools
|
|
86
|
-
| `openclaw ocsmarttools
|
|
87
|
-
| `openclaw ocsmarttools
|
|
88
|
-
| `openclaw ocsmarttools stats
|
|
89
|
-
| `openclaw ocsmarttools
|
|
91
|
+
| `openclaw ocsmarttools help` | Shows all commands in plain language |
|
|
92
|
+
| `openclaw ocsmarttools status` | Shows mode and safety settings |
|
|
93
|
+
| `openclaw ocsmarttools strict <on\|off>` | Turns strict routing on/off |
|
|
94
|
+
| `openclaw ocsmarttools sync` | Rewrites the managed routing block in `AGENTS.md` |
|
|
95
|
+
| `openclaw ocsmarttools stats` | Shows calls, errors, timeouts, latency, and estimated savings |
|
|
96
|
+
| `openclaw ocsmarttools stats reset` | Resets plugin stats |
|
|
97
|
+
| `openclaw ocsmarttools setup [safe\|standard]` | Applies recommended defaults |
|
|
90
98
|
| `openclaw ocsmarttools mode <safe\|standard>` | Changes mode only |
|
|
91
|
-
| `openclaw ocsmarttools
|
|
92
|
-
| `openclaw ocsmarttools config` | Shows effective plugin config |
|
|
99
|
+
| `openclaw ocsmarttools config` | Shows plugin config |
|
|
93
100
|
| `openclaw ocsmarttools config keys` | Lists editable config keys |
|
|
94
|
-
| `openclaw ocsmarttools config set <key> <value>` |
|
|
95
|
-
| `openclaw ocsmarttools config reset [key]` | Resets one key
|
|
101
|
+
| `openclaw ocsmarttools config set <key> <value>` | Changes one config value |
|
|
102
|
+
| `openclaw ocsmarttools config reset [key]` | Resets one key or all keys |
|
|
103
|
+
| `openclaw ocsmarttools version` | Shows installed plugin version |
|
|
96
104
|
|
|
97
105
|
## Common Config Actions
|
|
98
106
|
|
|
@@ -103,6 +111,7 @@ Model note:
|
|
|
103
111
|
/ocsmarttools config set storeLargeResults true
|
|
104
112
|
/ocsmarttools config set toolSearch.useLiveRegistry true
|
|
105
113
|
/ocsmarttools config set toolSearch.liveTimeoutMs 1500
|
|
114
|
+
/ocsmarttools config set strictRouting true
|
|
106
115
|
/ocsmarttools config set autoInjectRoutingGuide true
|
|
107
116
|
/ocsmarttools config set autoInjectRoutingGuide false
|
|
108
117
|
/ocsmarttools config reset maxResultChars
|
|
@@ -116,6 +125,7 @@ Config path:
|
|
|
116
125
|
|
|
117
126
|
- `standard` (default): zero-touch mode, no sandbox requirement, control-plane dispatch still blocked
|
|
118
127
|
- `safe`: requires sandboxed execution and blocks control-plane dispatch (`gateway`, `cron`)
|
|
128
|
+
- `strictRouting=true`: strict plugin-managed routing policy is auto-synced with no manual workspace edits.
|
|
119
129
|
|
|
120
130
|
Setup default:
|
|
121
131
|
- `/ocsmarttools setup` and `openclaw ocsmarttools setup` default to `standard`.
|
|
@@ -153,6 +163,7 @@ If your instance uses strict `tools.allow`, include:
|
|
|
153
163
|
|
|
154
164
|
- `ocsmarttools` does not bypass OpenClaw tool policy.
|
|
155
165
|
- Routing policy is auto-injected into `AGENTS.md` with managed markers; it can be re-synced via `/ocsmarttools sync`.
|
|
166
|
+
- `strictRouting=true` forces `autoInjectRoutingGuide=true`.
|
|
156
167
|
- `tool_batch` is intentionally bounded (`maxSteps`, `maxForEach`).
|
|
157
168
|
- Large-result handles are in-memory and expire by TTL.
|
|
158
169
|
- `tool_result_get` works only while handle is still valid.
|
package/openclaw.plugin.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"properties": {
|
|
9
9
|
"enabled": { "type": "boolean" },
|
|
10
10
|
"mode": { "type": "string", "enum": ["safe", "standard"] },
|
|
11
|
+
"strictRouting": { "type": "boolean" },
|
|
11
12
|
"autoInjectRoutingGuide": { "type": "boolean" },
|
|
12
13
|
"maxSteps": { "type": "integer", "minimum": 1, "maximum": 200 },
|
|
13
14
|
"maxForEach": { "type": "integer", "minimum": 1, "maximum": 200 },
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
},
|
|
33
34
|
"uiHints": {
|
|
34
35
|
"mode": { "label": "Mode" },
|
|
36
|
+
"strictRouting": { "label": "Strict Routing" },
|
|
35
37
|
"autoInjectRoutingGuide": { "label": "Auto Inject Routing Guide" },
|
|
36
38
|
"maxSteps": { "label": "Max Steps", "advanced": true },
|
|
37
39
|
"maxForEach": { "label": "Max ForEach", "advanced": true },
|
package/package.json
CHANGED
package/src/commands/chat.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resetConfig,
|
|
14
14
|
setConfigKey,
|
|
15
15
|
syncManagedRouting,
|
|
16
|
+
updateStrictRouting,
|
|
16
17
|
updateMode,
|
|
17
18
|
} from "./operations.js";
|
|
18
19
|
import type { AdvToolsMode } from "../lib/plugin-config.js";
|
|
@@ -73,6 +74,20 @@ export function registerChatCommands(api: OpenClawPluginApi, metrics: MetricsSto
|
|
|
73
74
|
return { text: await updateMode(api, mode) };
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
if (cmd === "strict") {
|
|
78
|
+
const arg = (parts[1] ?? "status").toLowerCase();
|
|
79
|
+
if (arg === "status") {
|
|
80
|
+
return { text: renderStatus(api) };
|
|
81
|
+
}
|
|
82
|
+
if (arg === "on" || arg === "true" || arg === "enable") {
|
|
83
|
+
return { text: await updateStrictRouting(api, true) };
|
|
84
|
+
}
|
|
85
|
+
if (arg === "off" || arg === "false" || arg === "disable") {
|
|
86
|
+
return { text: await updateStrictRouting(api, false) };
|
|
87
|
+
}
|
|
88
|
+
return { text: "Usage: /ocsmarttools strict <on|off|status>" };
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
if (cmd === "sync") {
|
|
77
92
|
return { text: await syncManagedRouting(api) };
|
|
78
93
|
}
|
|
@@ -112,6 +127,7 @@ export function registerChatCommands(api: OpenClawPluginApi, metrics: MetricsSto
|
|
|
112
127
|
"/ocsmarttools config set <key> <value>",
|
|
113
128
|
"/ocsmarttools config reset [key]",
|
|
114
129
|
"/ocsmarttools sync",
|
|
130
|
+
"/ocsmarttools strict <on|off|status>",
|
|
115
131
|
].join("\n"),
|
|
116
132
|
};
|
|
117
133
|
}
|
package/src/commands/cli.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resetConfig,
|
|
12
12
|
setConfigKey,
|
|
13
13
|
syncManagedRouting,
|
|
14
|
+
updateStrictRouting,
|
|
14
15
|
updateMode,
|
|
15
16
|
} from "./operations.js";
|
|
16
17
|
import type { AdvToolsMode } from "../lib/plugin-config.js";
|
|
@@ -98,6 +99,24 @@ export function registerCliCommands(api: OpenClawPluginApi, metrics: MetricsStor
|
|
|
98
99
|
console.log(text);
|
|
99
100
|
});
|
|
100
101
|
|
|
102
|
+
adv
|
|
103
|
+
.command("strict <state>")
|
|
104
|
+
.description("Set strict routing on/off (state: on|off)")
|
|
105
|
+
.action(async (stateRaw: string) => {
|
|
106
|
+
const state = stateRaw.trim().toLowerCase();
|
|
107
|
+
if (["on", "true", "enable"].includes(state)) {
|
|
108
|
+
// eslint-disable-next-line no-console
|
|
109
|
+
console.log(await updateStrictRouting(api, true));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (["off", "false", "disable"].includes(state)) {
|
|
113
|
+
// eslint-disable-next-line no-console
|
|
114
|
+
console.log(await updateStrictRouting(api, false));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
throw new Error("strict state must be one of: on, off");
|
|
118
|
+
});
|
|
119
|
+
|
|
101
120
|
adv
|
|
102
121
|
.command("config")
|
|
103
122
|
.description("Show effective plugin config")
|
|
@@ -21,6 +21,7 @@ type ConfigSpec =
|
|
|
21
21
|
const CONFIG_SPECS: Record<string, ConfigSpec> = {
|
|
22
22
|
enabled: { kind: "boolean" },
|
|
23
23
|
mode: { kind: "enum", values: ["safe", "standard"] },
|
|
24
|
+
strictRouting: { kind: "boolean" },
|
|
24
25
|
autoInjectRoutingGuide: { kind: "boolean" },
|
|
25
26
|
maxSteps: { kind: "integer", min: 1, max: 200 },
|
|
26
27
|
maxForEach: { kind: "integer", min: 1, max: 200 },
|
|
@@ -40,6 +41,7 @@ const CONFIG_SPECS: Record<string, ConfigSpec> = {
|
|
|
40
41
|
const DEFAULT_BY_KEY: Record<string, boolean | number | string> = {
|
|
41
42
|
enabled: DEFAULT_SETTINGS.enabled,
|
|
42
43
|
mode: DEFAULT_SETTINGS.mode,
|
|
44
|
+
strictRouting: DEFAULT_SETTINGS.strictRouting,
|
|
43
45
|
autoInjectRoutingGuide: DEFAULT_SETTINGS.autoInjectRoutingGuide,
|
|
44
46
|
maxSteps: DEFAULT_SETTINGS.maxSteps,
|
|
45
47
|
maxForEach: DEFAULT_SETTINGS.maxForEach,
|
|
@@ -143,6 +145,7 @@ export function renderStatus(api: OpenClawPluginApi): string {
|
|
|
143
145
|
"OCSmartTools Status",
|
|
144
146
|
`- plugin: ${api.id}`,
|
|
145
147
|
`- mode: ${s.mode}`,
|
|
148
|
+
`- strictRouting: ${s.strictRouting}`,
|
|
146
149
|
`- autoInjectRoutingGuide: ${s.autoInjectRoutingGuide}`,
|
|
147
150
|
`- tool_search enabled: ${s.toolSearch.enabled}`,
|
|
148
151
|
`- maxSteps: ${s.maxSteps}`,
|
|
@@ -159,26 +162,31 @@ export function renderStatus(api: OpenClawPluginApi): string {
|
|
|
159
162
|
|
|
160
163
|
export function renderHelp(): string {
|
|
161
164
|
return [
|
|
162
|
-
"OCSmartTools Help",
|
|
165
|
+
"OCSmartTools Help (Simple)",
|
|
163
166
|
"",
|
|
164
|
-
"
|
|
165
|
-
"- /ocsmarttools
|
|
166
|
-
"- /ocsmarttools
|
|
167
|
-
"- /ocsmarttools
|
|
168
|
-
"- /ocsmarttools
|
|
169
|
-
"- /ocsmarttools
|
|
170
|
-
"- /ocsmarttools
|
|
171
|
-
"- /ocsmarttools
|
|
172
|
-
"- /ocsmarttools
|
|
173
|
-
"- /ocsmarttools
|
|
174
|
-
"- /ocsmarttools config
|
|
175
|
-
"- /ocsmarttools config
|
|
176
|
-
"- /ocsmarttools config
|
|
167
|
+
"Use these in chat:",
|
|
168
|
+
"- /ocsmarttools status -> Shows current mode and safety settings.",
|
|
169
|
+
"- /ocsmarttools strict on -> Turns on strict plugin-managed routing.",
|
|
170
|
+
"- /ocsmarttools strict off -> Turns strict routing off.",
|
|
171
|
+
"- /ocsmarttools strict status -> Shows whether strict routing is on or off.",
|
|
172
|
+
"- /ocsmarttools sync -> Rewrites/refreshes the managed routing block in AGENTS.md.",
|
|
173
|
+
"- /ocsmarttools stats -> Shows calls, errors, timeouts, latency, and savings.",
|
|
174
|
+
"- /ocsmarttools stats reset -> Resets the stats window to zero.",
|
|
175
|
+
"- /ocsmarttools setup [safe|standard] -> Applies recommended defaults.",
|
|
176
|
+
"- /ocsmarttools mode <safe|standard> -> Changes only mode.",
|
|
177
|
+
"- /ocsmarttools config -> Shows current plugin config.",
|
|
178
|
+
"- /ocsmarttools config keys -> Lists editable config keys.",
|
|
179
|
+
"- /ocsmarttools config set <key> <value> -> Changes one config value.",
|
|
180
|
+
"- /ocsmarttools config reset [key] -> Resets one key or all keys.",
|
|
181
|
+
"- /ocsmarttools version -> Shows installed plugin version.",
|
|
182
|
+
"- /ocsmarttools help -> Shows this help message.",
|
|
177
183
|
"",
|
|
178
|
-
"
|
|
184
|
+
"Quick examples:",
|
|
185
|
+
"- /ocsmarttools status",
|
|
179
186
|
"- /ocsmarttools config set maxResultChars 120000",
|
|
180
|
-
"- /ocsmarttools
|
|
181
|
-
"- /ocsmarttools
|
|
187
|
+
"- /ocsmarttools strict on",
|
|
188
|
+
"- /ocsmarttools stats",
|
|
189
|
+
"- /ocsmarttools sync",
|
|
182
190
|
].join("\n");
|
|
183
191
|
}
|
|
184
192
|
|
|
@@ -234,6 +242,7 @@ export function renderConfig(api: OpenClawPluginApi): string {
|
|
|
234
242
|
"OCSmartTools Config",
|
|
235
243
|
`- enabled: ${s.enabled}`,
|
|
236
244
|
`- mode: ${s.mode}`,
|
|
245
|
+
`- strictRouting: ${s.strictRouting}`,
|
|
237
246
|
`- autoInjectRoutingGuide: ${s.autoInjectRoutingGuide}`,
|
|
238
247
|
`- maxSteps: ${s.maxSteps}`,
|
|
239
248
|
`- maxForEach: ${s.maxForEach}`,
|
|
@@ -277,10 +286,18 @@ export async function setConfigKey(
|
|
|
277
286
|
entryObj.config = pluginCfg;
|
|
278
287
|
|
|
279
288
|
setValueAtPath(pluginCfg, key, parsed.value);
|
|
289
|
+
if (key === "strictRouting" && parsed.value === true) {
|
|
290
|
+
setValueAtPath(pluginCfg, "autoInjectRoutingGuide", true);
|
|
291
|
+
}
|
|
280
292
|
await writeConfig(api, next);
|
|
281
293
|
|
|
282
|
-
if (key === "autoInjectRoutingGuide"
|
|
283
|
-
const
|
|
294
|
+
if (key === "autoInjectRoutingGuide" || key === "strictRouting") {
|
|
295
|
+
const strictRouting = resolveSettings(api, next).strictRouting;
|
|
296
|
+
const shouldSync = resolveSettings(api, next).autoInjectRoutingGuide;
|
|
297
|
+
if (!shouldSync) {
|
|
298
|
+
return `Config updated: ${key}=${JSON.stringify(parsed.value)}.`;
|
|
299
|
+
}
|
|
300
|
+
const sync = await syncRoutingGuide(api, next, { strictRouting });
|
|
284
301
|
if (sync.error) {
|
|
285
302
|
return `Config updated: ${key}=${JSON.stringify(parsed.value)} (routing sync skipped: ${sync.error}).`;
|
|
286
303
|
}
|
|
@@ -300,9 +317,14 @@ export async function resetConfig(api: OpenClawPluginApi, key?: string): Promise
|
|
|
300
317
|
for (const cfgKey of sortedKeys()) {
|
|
301
318
|
setValueAtPath(pluginCfg, cfgKey, DEFAULT_BY_KEY[cfgKey]);
|
|
302
319
|
}
|
|
320
|
+
if (DEFAULT_SETTINGS.strictRouting) {
|
|
321
|
+
setValueAtPath(pluginCfg, "autoInjectRoutingGuide", true);
|
|
322
|
+
}
|
|
303
323
|
await writeConfig(api, next);
|
|
304
|
-
if (
|
|
305
|
-
const sync = await syncRoutingGuide(api, next
|
|
324
|
+
if (resolveSettings(api, next).autoInjectRoutingGuide) {
|
|
325
|
+
const sync = await syncRoutingGuide(api, next, {
|
|
326
|
+
strictRouting: resolveSettings(api, next).strictRouting,
|
|
327
|
+
});
|
|
306
328
|
if (sync.error) {
|
|
307
329
|
return `Config reset to plugin defaults (routing sync skipped: ${sync.error}).`;
|
|
308
330
|
}
|
|
@@ -315,9 +337,17 @@ export async function resetConfig(api: OpenClawPluginApi, key?: string): Promise
|
|
|
315
337
|
}
|
|
316
338
|
|
|
317
339
|
setValueAtPath(pluginCfg, key, DEFAULT_BY_KEY[key]);
|
|
340
|
+
if (key === "strictRouting" && DEFAULT_BY_KEY[key] === true) {
|
|
341
|
+
setValueAtPath(pluginCfg, "autoInjectRoutingGuide", true);
|
|
342
|
+
}
|
|
318
343
|
await writeConfig(api, next);
|
|
319
|
-
if (key === "autoInjectRoutingGuide"
|
|
320
|
-
const
|
|
344
|
+
if (key === "autoInjectRoutingGuide" || key === "strictRouting") {
|
|
345
|
+
const strictRouting = resolveSettings(api, next).strictRouting;
|
|
346
|
+
const shouldSync = resolveSettings(api, next).autoInjectRoutingGuide;
|
|
347
|
+
if (!shouldSync) {
|
|
348
|
+
return `Config key reset: ${key}=${JSON.stringify(DEFAULT_BY_KEY[key])}.`;
|
|
349
|
+
}
|
|
350
|
+
const sync = await syncRoutingGuide(api, next, { strictRouting });
|
|
321
351
|
if (sync.error) {
|
|
322
352
|
return `Config key reset: ${key}=${JSON.stringify(DEFAULT_BY_KEY[key])} (routing sync skipped: ${sync.error}).`;
|
|
323
353
|
}
|
|
@@ -345,6 +375,7 @@ export async function applySetup(api: OpenClawPluginApi, mode: AdvToolsMode): Pr
|
|
|
345
375
|
|
|
346
376
|
pluginCfg.enabled = true;
|
|
347
377
|
pluginCfg.mode = mode;
|
|
378
|
+
pluginCfg.strictRouting = false;
|
|
348
379
|
pluginCfg.autoInjectRoutingGuide = true;
|
|
349
380
|
pluginCfg.maxSteps = DEFAULT_SETTINGS.maxSteps;
|
|
350
381
|
pluginCfg.maxForEach = DEFAULT_SETTINGS.maxForEach;
|
|
@@ -370,7 +401,7 @@ export async function applySetup(api: OpenClawPluginApi, mode: AdvToolsMode): Pr
|
|
|
370
401
|
);
|
|
371
402
|
|
|
372
403
|
await writeConfig(api, next);
|
|
373
|
-
const sync = await syncRoutingGuide(api, next);
|
|
404
|
+
const sync = await syncRoutingGuide(api, next, { strictRouting: false });
|
|
374
405
|
const syncLine = sync.error
|
|
375
406
|
? `- routing guide sync skipped: ${sync.error}`
|
|
376
407
|
: `- routing guide ${sync.changed ? "synced" : "already up to date"} (${sync.filePath ?? "AGENTS.md"})`;
|
|
@@ -401,13 +432,35 @@ export async function updateMode(api: OpenClawPluginApi, mode: AdvToolsMode): Pr
|
|
|
401
432
|
return `Mode updated to ${mode}.`;
|
|
402
433
|
}
|
|
403
434
|
|
|
435
|
+
export async function updateStrictRouting(api: OpenClawPluginApi, enabled: boolean): Promise<string> {
|
|
436
|
+
const next = deepCloneConfig(api.runtime.config.loadConfig());
|
|
437
|
+
const { entryObj } = ensurePluginEntry(next, api.id);
|
|
438
|
+
entryObj.enabled = true;
|
|
439
|
+
|
|
440
|
+
const pluginCfg = asObj(entryObj.config);
|
|
441
|
+
entryObj.config = pluginCfg;
|
|
442
|
+
pluginCfg.strictRouting = enabled;
|
|
443
|
+
if (enabled) {
|
|
444
|
+
pluginCfg.autoInjectRoutingGuide = true;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
await writeConfig(api, next);
|
|
448
|
+
if (resolveSettings(api, next).autoInjectRoutingGuide) {
|
|
449
|
+
const sync = await syncRoutingGuide(api, next, { strictRouting: enabled });
|
|
450
|
+
if (sync.error) {
|
|
451
|
+
return `Strict routing ${enabled ? "enabled" : "disabled"} (routing sync skipped: ${sync.error}).`;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return `Strict routing ${enabled ? "enabled" : "disabled"}.`;
|
|
455
|
+
}
|
|
456
|
+
|
|
404
457
|
export async function syncManagedRouting(api: OpenClawPluginApi): Promise<string> {
|
|
405
458
|
const loaded = api.runtime.config.loadConfig();
|
|
406
459
|
const settings = resolveSettings(api, loaded);
|
|
407
460
|
if (!settings.autoInjectRoutingGuide) {
|
|
408
461
|
return "Routing guide sync skipped: autoInjectRoutingGuide=false.";
|
|
409
462
|
}
|
|
410
|
-
const result = await syncRoutingGuide(api, loaded);
|
|
463
|
+
const result = await syncRoutingGuide(api, loaded, { strictRouting: settings.strictRouting });
|
|
411
464
|
if (result.error) {
|
|
412
465
|
return `Routing guide sync skipped: ${result.error}`;
|
|
413
466
|
}
|
package/src/lib/bootstrap.ts
CHANGED
|
@@ -46,6 +46,7 @@ export async function autoBootstrap(api: OpenClawPluginApi): Promise<{ changed:
|
|
|
46
46
|
|
|
47
47
|
setDefault("enabled", true);
|
|
48
48
|
setDefault("mode", DEFAULT_SETTINGS.mode);
|
|
49
|
+
setDefault("strictRouting", DEFAULT_SETTINGS.strictRouting);
|
|
49
50
|
setDefault("autoInjectRoutingGuide", DEFAULT_SETTINGS.autoInjectRoutingGuide);
|
|
50
51
|
setDefault("maxSteps", DEFAULT_SETTINGS.maxSteps);
|
|
51
52
|
setDefault("maxForEach", DEFAULT_SETTINGS.maxForEach);
|
|
@@ -113,12 +114,21 @@ export async function autoBootstrap(api: OpenClawPluginApi): Promise<{ changed:
|
|
|
113
114
|
await writeConfig(api, next);
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
const strictRouting =
|
|
118
|
+
typeof pluginCfg.strictRouting === "boolean" ? pluginCfg.strictRouting : DEFAULT_SETTINGS.strictRouting;
|
|
119
|
+
if (strictRouting && pluginCfg.autoInjectRoutingGuide !== true) {
|
|
120
|
+
pluginCfg.autoInjectRoutingGuide = true;
|
|
121
|
+
changed = true;
|
|
122
|
+
notes.push("forced autoInjectRoutingGuide=true for strictRouting");
|
|
123
|
+
await writeConfig(api, next);
|
|
124
|
+
}
|
|
125
|
+
|
|
116
126
|
const routingGuideEnabled =
|
|
117
127
|
typeof pluginCfg.autoInjectRoutingGuide === "boolean"
|
|
118
128
|
? pluginCfg.autoInjectRoutingGuide
|
|
119
129
|
: DEFAULT_SETTINGS.autoInjectRoutingGuide;
|
|
120
130
|
if (routingGuideEnabled) {
|
|
121
|
-
const sync = await syncRoutingGuide(api, next);
|
|
131
|
+
const sync = await syncRoutingGuide(api, next, { strictRouting });
|
|
122
132
|
if (sync.error) {
|
|
123
133
|
notes.push(`routing guide skipped: ${sync.error}`);
|
|
124
134
|
} else if (sync.changed) {
|
package/src/lib/plugin-config.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type AdvToolsMode = "safe" | "standard";
|
|
|
5
5
|
export type AdvToolsSettings = {
|
|
6
6
|
enabled: boolean;
|
|
7
7
|
mode: AdvToolsMode;
|
|
8
|
+
strictRouting: boolean;
|
|
8
9
|
autoInjectRoutingGuide: boolean;
|
|
9
10
|
maxSteps: number;
|
|
10
11
|
maxForEach: number;
|
|
@@ -26,6 +27,7 @@ export type AdvToolsSettings = {
|
|
|
26
27
|
export const DEFAULT_SETTINGS: AdvToolsSettings = {
|
|
27
28
|
enabled: true,
|
|
28
29
|
mode: "standard",
|
|
30
|
+
strictRouting: false,
|
|
29
31
|
autoInjectRoutingGuide: true,
|
|
30
32
|
maxSteps: 25,
|
|
31
33
|
maxForEach: 20,
|
|
@@ -72,13 +74,16 @@ export function resolveSettings(api: OpenClawPluginApi, cfg: OpenClawConfig = ap
|
|
|
72
74
|
const pluginCfg = asObj(entry.config ?? api.pluginConfig);
|
|
73
75
|
const ts = asObj(pluginCfg.toolSearch);
|
|
74
76
|
|
|
77
|
+
const strictRouting = asBool(pluginCfg.strictRouting, DEFAULT_SETTINGS.strictRouting);
|
|
78
|
+
const autoInjectRoutingGuide = strictRouting
|
|
79
|
+
? true
|
|
80
|
+
: asBool(pluginCfg.autoInjectRoutingGuide, DEFAULT_SETTINGS.autoInjectRoutingGuide);
|
|
81
|
+
|
|
75
82
|
return {
|
|
76
83
|
enabled: asBool(pluginCfg.enabled, DEFAULT_SETTINGS.enabled),
|
|
77
84
|
mode: asMode(pluginCfg.mode, DEFAULT_SETTINGS.mode),
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
DEFAULT_SETTINGS.autoInjectRoutingGuide,
|
|
81
|
-
),
|
|
85
|
+
strictRouting,
|
|
86
|
+
autoInjectRoutingGuide,
|
|
82
87
|
maxSteps: asInt(pluginCfg.maxSteps, DEFAULT_SETTINGS.maxSteps, 1, 200),
|
|
83
88
|
maxForEach: asInt(pluginCfg.maxForEach, DEFAULT_SETTINGS.maxForEach, 1, 200),
|
|
84
89
|
maxResultChars: asInt(pluginCfg.maxResultChars, DEFAULT_SETTINGS.maxResultChars, 500, 500000),
|
package/src/lib/routing-guide.ts
CHANGED
|
@@ -6,7 +6,60 @@ import type { OpenClawConfig, OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
|
6
6
|
const BLOCK_START = "<!-- OCSMARTTOOLS_ROUTING_START -->";
|
|
7
7
|
const BLOCK_END = "<!-- OCSMARTTOOLS_ROUTING_END -->";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
function buildRoutingBlock(strictRouting: boolean): string {
|
|
10
|
+
const body = strictRouting
|
|
11
|
+
? [
|
|
12
|
+
"## OCSmartTools Routing Policy (Auto-Managed, Strict)",
|
|
13
|
+
"",
|
|
14
|
+
"Default objective: preserve answer quality while reducing token and latency cost.",
|
|
15
|
+
"",
|
|
16
|
+
"Mandatory rules in strict mode:",
|
|
17
|
+
"1. Route tool usage through `tool_dispatch` or `tool_batch`.",
|
|
18
|
+
"2. Use `tool_batch` for 2+ related calls, iterative steps, or map-style tasks.",
|
|
19
|
+
"3. Use `tool_search` only when tool choice is unclear.",
|
|
20
|
+
"4. Use `tool_result_get` only when a stored handle needs more detail.",
|
|
21
|
+
"5. Avoid direct native tool calls unless routing tools are unavailable due to runtime/tool-policy constraints.",
|
|
22
|
+
"",
|
|
23
|
+
"Common large/noisy tools: `web_fetch`, `read` (large files), `exec`, `process`, `browser`, `nodes`.",
|
|
24
|
+
].join("\n")
|
|
25
|
+
: [
|
|
26
|
+
"## OCSmartTools Routing Policy (Auto-Managed)",
|
|
27
|
+
"",
|
|
28
|
+
"Default objective: preserve answer quality while reducing token and latency cost.",
|
|
29
|
+
"",
|
|
30
|
+
"1. If tool usage is needed and result size is uncertain, use `tool_dispatch`.",
|
|
31
|
+
"2. If the task needs 2+ related tool calls, use `tool_batch`.",
|
|
32
|
+
"3. Use `tool_search` only when tool choice is unclear.",
|
|
33
|
+
"4. Prefer compact/tool-shaped outputs; use `tool_result_get` only when more detail is required.",
|
|
34
|
+
"5. Use direct native tool calls only for simple one-shot small-output actions.",
|
|
35
|
+
"",
|
|
36
|
+
"Common large/noisy tools: `web_fetch`, `read` (large files), `exec`, `process`, `browser`, `nodes`.",
|
|
37
|
+
].join("\n");
|
|
38
|
+
|
|
39
|
+
return `${BLOCK_START}
|
|
40
|
+
${body}
|
|
41
|
+
${BLOCK_END}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DEFAULT_BASE_CONTENT = "# AGENTS.md - Workspace Directives\n\nAdd local operating preferences below.\n";
|
|
46
|
+
|
|
47
|
+
function upsertRoutingBlock(raw: string, strictRouting: boolean): string {
|
|
48
|
+
const routingBlock = buildRoutingBlock(strictRouting);
|
|
49
|
+
const source = raw.trim() ? raw : DEFAULT_BASE_CONTENT;
|
|
50
|
+
|
|
51
|
+
const start = source.indexOf(BLOCK_START);
|
|
52
|
+
const end = source.indexOf(BLOCK_END);
|
|
53
|
+
if (start >= 0 && end > start) {
|
|
54
|
+
const before = source.slice(0, start).replace(/\s*$/, "");
|
|
55
|
+
const after = source.slice(end + BLOCK_END.length).replace(/^\s*/, "");
|
|
56
|
+
return `${before}\n\n${routingBlock}\n${after}`.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
57
|
+
}
|
|
58
|
+
const joined = `${source.replace(/\s*$/, "")}\n\n${routingBlock}\n`;
|
|
59
|
+
return joined.replace(/\n{3,}/g, "\n\n");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const LEGACY_ROUTING_BLOCK = `${BLOCK_START}
|
|
10
63
|
## OCSmartTools Routing Policy (Auto-Managed)
|
|
11
64
|
|
|
12
65
|
Default objective: preserve answer quality while reducing token and latency cost.
|
|
@@ -41,23 +94,11 @@ function resolveWorkspaceDir(cfg: OpenClawConfig): string {
|
|
|
41
94
|
return path.join(os.homedir(), ".openclaw", "workspace");
|
|
42
95
|
}
|
|
43
96
|
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const start = source.indexOf(BLOCK_START);
|
|
50
|
-
const end = source.indexOf(BLOCK_END);
|
|
51
|
-
if (start >= 0 && end > start) {
|
|
52
|
-
const before = source.slice(0, start).replace(/\s*$/, "");
|
|
53
|
-
const after = source.slice(end + BLOCK_END.length).replace(/^\s*/, "");
|
|
54
|
-
return `${before}\n\n${ROUTING_BLOCK}\n${after}`.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
55
|
-
}
|
|
56
|
-
const joined = `${source.replace(/\s*$/, "")}\n\n${ROUTING_BLOCK}\n`;
|
|
57
|
-
return joined.replace(/\n{3,}/g, "\n\n");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function syncRoutingGuide(api: OpenClawPluginApi, cfg: OpenClawConfig): Promise<{
|
|
97
|
+
export async function syncRoutingGuide(
|
|
98
|
+
api: OpenClawPluginApi,
|
|
99
|
+
cfg: OpenClawConfig,
|
|
100
|
+
options?: { strictRouting?: boolean },
|
|
101
|
+
): Promise<{
|
|
61
102
|
changed: boolean;
|
|
62
103
|
filePath?: string;
|
|
63
104
|
error?: string;
|
|
@@ -77,8 +118,13 @@ export async function syncRoutingGuide(api: OpenClawPluginApi, cfg: OpenClawConf
|
|
|
77
118
|
}
|
|
78
119
|
}
|
|
79
120
|
|
|
80
|
-
const
|
|
81
|
-
|
|
121
|
+
const strictRouting = options?.strictRouting === true;
|
|
122
|
+
// If a legacy block exists, treat it as managed content and replace it with the latest variant.
|
|
123
|
+
const normalizedCurrent = current.includes(LEGACY_ROUTING_BLOCK)
|
|
124
|
+
? current.replace(LEGACY_ROUTING_BLOCK, buildRoutingBlock(false))
|
|
125
|
+
: current;
|
|
126
|
+
const next = upsertRoutingBlock(normalizedCurrent, strictRouting);
|
|
127
|
+
if (next === current || next === normalizedCurrent) {
|
|
82
128
|
return { changed: false, filePath };
|
|
83
129
|
}
|
|
84
130
|
|