@senpi/runtime 3.0.9-dev.feat-strategy-compos.20260721003344 → 3.0.9-dev.feat-strategy-compos.20260722213012
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/composer-commands.d.ts.map +1 -1
- package/dist/cli/composer-commands.js +193 -41
- package/dist/cli/composer-commands.js.map +1 -1
- package/dist/cli/composer-live-read.d.ts +68 -0
- package/dist/cli/composer-live-read.d.ts.map +1 -0
- package/dist/cli/composer-live-read.js +126 -0
- package/dist/cli/composer-live-read.js.map +1 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +8 -10
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/cli/senpi-strategy-tool.d.ts +56 -0
- package/dist/cli/senpi-strategy-tool.d.ts.map +1 -0
- package/dist/cli/senpi-strategy-tool.js +158 -0
- package/dist/cli/senpi-strategy-tool.js.map +1 -0
- package/dist/composer/authoring/__init__.py +2 -0
- package/dist/composer/authoring/answers.py +52 -0
- package/dist/composer/authoring/archetypes/__init__.py +2 -0
- package/dist/composer/authoring/archetypes/contrarian_fade.py +493 -0
- package/dist/composer/authoring/archetypes/relative_value.py +280 -23
- package/dist/composer/cli.py +57 -6
- package/dist/composer/examples/contrarian_fade_crowd.graph.yaml +356 -0
- package/dist/composer/examples/rotation_cross_sectional.graph.yaml +389 -0
- package/dist/composer/generated/catalog.md +2 -1
- package/dist/composer/registry/generate.py +2 -1
- package/dist/composer/validator/errors.py +2 -0
- package/dist/composer/verbs/__init__.py +6 -0
- package/dist/composer/verbs/_box.py +96 -5
- package/dist/composer/verbs/_layout.py +37 -0
- package/dist/composer/verbs/_live.py +50 -6
- package/dist/composer/verbs/close.py +51 -6
- package/dist/composer/verbs/fund.py +144 -27
- package/dist/composer/verbs/install.py +25 -6
- package/dist/composer/verbs/portfolio.py +650 -0
- package/dist/composer/verbs/review.py +724 -0
- package/dist/composer/verbs/status.py +129 -2
- package/dist/composer/verbs/update.py +9 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/composer-managed-write.d.ts +133 -0
- package/dist/runtime/composer-managed-write.d.ts.map +1 -0
- package/dist/runtime/composer-managed-write.js +369 -0
- package/dist/runtime/composer-managed-write.js.map +1 -0
- package/openclaw.plugin.json +5 -0
- package/package.json +2 -1
|
@@ -53,7 +53,7 @@ from typing import Optional
|
|
|
53
53
|
from composer.authoring import emit
|
|
54
54
|
|
|
55
55
|
from . import _box, _live
|
|
56
|
-
from ._box import ListFn, runtime_running, wallet_match
|
|
56
|
+
from ._box import ListFn, PositionsFn, runtime_running, wallet_match
|
|
57
57
|
from ._live import (
|
|
58
58
|
LiveState,
|
|
59
59
|
SystemStateFn,
|
|
@@ -64,13 +64,17 @@ from ._live import (
|
|
|
64
64
|
)
|
|
65
65
|
from ._layout import (
|
|
66
66
|
COMPOSER_DIR,
|
|
67
|
+
KIND_MANAGED,
|
|
68
|
+
KIND_MANUAL,
|
|
67
69
|
LEGACY_STRATEGIES_DIR,
|
|
68
70
|
archives_dir,
|
|
69
71
|
canonical_graph_path,
|
|
70
72
|
canonical_releases_dir,
|
|
71
73
|
canonical_staged_dir,
|
|
74
|
+
canonical_wallet_json,
|
|
72
75
|
is_canonical_graph,
|
|
73
76
|
iter_release_dirs,
|
|
77
|
+
read_strategy_kind,
|
|
74
78
|
resolve_wallet_json,
|
|
75
79
|
runtime_state_dir,
|
|
76
80
|
)
|
|
@@ -86,6 +90,10 @@ INSTALLABLE = "installable"
|
|
|
86
90
|
INSTALLED_UNREGISTERED = "installed_unregistered"
|
|
87
91
|
INSTALLED_STOPPED = "installed_stopped"
|
|
88
92
|
INSTALLED_RUNNING = "installed_running"
|
|
93
|
+
# J3: a MANUAL strategy — a funded wallet with NO graph/runtime, under the composer record system.
|
|
94
|
+
# It has its own terminal-ish position (there is no lifecycle to advance): the sanctioned action is
|
|
95
|
+
# to edit positions directly via MCP, or `composer close` to tear it down.
|
|
96
|
+
MANUAL = "manual"
|
|
89
97
|
|
|
90
98
|
# Honest liveness caveat appended to a running install (process-level ≠ functional liveness).
|
|
91
99
|
LIVE_NOT_CHECKED = (
|
|
@@ -103,6 +111,7 @@ _NEXT = {
|
|
|
103
111
|
INSTALLED_UNREGISTERED: ("composer install", "landed on the box but the runtime was never registered (interrupted install) — re-run `composer install` to finish it."),
|
|
104
112
|
INSTALLED_STOPPED: ("composer install", "installed + registered but the runtime is NOT running — re-run `composer install`, or check `openclaw senpi runtime list`."),
|
|
105
113
|
INSTALLED_RUNNING: ("(none)", "installed and running — observe the live tick (`openclaw senpi state -r <id>` for functional liveness)."),
|
|
114
|
+
MANUAL: ("(none)", "manual strategy — a wallet with NO runtime; edit positions directly via MCP (the sanctioned path for this kind), or `composer close` to tear it down and return funds."),
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
|
|
@@ -185,6 +194,14 @@ class StatusResult:
|
|
|
185
194
|
live: Optional[LiveState] = None
|
|
186
195
|
sizing: dict = field(default_factory=dict)
|
|
187
196
|
unmanaged_runtimes: list[str] = field(default_factory=list)
|
|
197
|
+
# J3: the strategy KIND predicate (managed = a graph strategy; manual = a wallet with no
|
|
198
|
+
# runtime). Carried in --json on every on-box status row so hooks/doctrine (J4/J5) get a
|
|
199
|
+
# machine-readable kind. The manual fields below are populated only for a MANUAL strategy.
|
|
200
|
+
kind: str = KIND_MANAGED
|
|
201
|
+
wallet: Optional[str] = None
|
|
202
|
+
strategy_id: Optional[str] = None # the backend strategy-record pointer (manual)
|
|
203
|
+
manual_positions: Optional[list[dict]] = None # None = not read; [] = read + flat
|
|
204
|
+
manual_positions_reason: str = "" # why positions are unavailable (LOUD, never assumed)
|
|
188
205
|
|
|
189
206
|
@property
|
|
190
207
|
def next_verb(self) -> str:
|
|
@@ -199,6 +216,8 @@ class StatusResult:
|
|
|
199
216
|
return next((i for i in self.installs if i.is_current), None)
|
|
200
217
|
|
|
201
218
|
def to_json(self) -> dict:
|
|
219
|
+
if self.position == MANUAL:
|
|
220
|
+
return self._manual_to_json()
|
|
202
221
|
out = {
|
|
203
222
|
"verb": "status",
|
|
204
223
|
"target": self.target,
|
|
@@ -225,6 +244,9 @@ class StatusResult:
|
|
|
225
244
|
out["paths"] = self.paths
|
|
226
245
|
if self.split_warning:
|
|
227
246
|
out["split_warning"] = self.split_warning
|
|
247
|
+
# J3 predicate: the kind rides on every ON-BOX status row (state root known) so hooks and
|
|
248
|
+
# doctrine (J4/J5) can branch on managed-vs-manual without re-deriving it.
|
|
249
|
+
out["kind"] = self.kind
|
|
228
250
|
if self.unmanaged_runtimes:
|
|
229
251
|
out["unmanaged_runtimes"] = self.unmanaged_runtimes
|
|
230
252
|
if self.live is not None:
|
|
@@ -234,7 +256,32 @@ class StatusResult:
|
|
|
234
256
|
out["live"] = live
|
|
235
257
|
return out
|
|
236
258
|
|
|
259
|
+
def _manual_to_json(self) -> dict:
|
|
260
|
+
"""The MANUAL-kind status shape (J3): a wallet + backend-record pointer, honest about the
|
|
261
|
+
absence of a runtime, and the wallet's open positions when the seam is wired."""
|
|
262
|
+
return {
|
|
263
|
+
"verb": "status",
|
|
264
|
+
"target": self.target,
|
|
265
|
+
"exists": True,
|
|
266
|
+
"position": MANUAL,
|
|
267
|
+
"kind": KIND_MANUAL,
|
|
268
|
+
"strategy": self.strategy,
|
|
269
|
+
"wallet": self.wallet,
|
|
270
|
+
"strategy_id": self.strategy_id,
|
|
271
|
+
"runtime": "none (manual by design — no runtime)",
|
|
272
|
+
"positions": {
|
|
273
|
+
"available": self.manual_positions is not None,
|
|
274
|
+
"reason": self.manual_positions_reason,
|
|
275
|
+
"positions": list(self.manual_positions or []),
|
|
276
|
+
},
|
|
277
|
+
"sanctioned_writes": "direct MCP position edits on this wallet",
|
|
278
|
+
"next_verb": self.next_verb,
|
|
279
|
+
"next": self.next_hint,
|
|
280
|
+
}
|
|
281
|
+
|
|
237
282
|
def render(self) -> str:
|
|
283
|
+
if self.position == MANUAL:
|
|
284
|
+
return self._render_manual()
|
|
238
285
|
lines = [f"status: {self.target}", f" position : {self.position}"]
|
|
239
286
|
if self.exists:
|
|
240
287
|
lines.append(f" provenance : {self.provenance}")
|
|
@@ -280,6 +327,40 @@ class StatusResult:
|
|
|
280
327
|
lines.append(f" next : {self.next_hint}")
|
|
281
328
|
return "\n".join(lines) + "\n"
|
|
282
329
|
|
|
330
|
+
def _render_manual(self) -> str:
|
|
331
|
+
"""The MANUAL-kind view (J3): render what IS true — wallet, backend-record pointer, an
|
|
332
|
+
honest 'no runtime — manual by design', and the wallet's open positions via the live seam.
|
|
333
|
+
There are NO protection lines: a manual strategy has no runtime/DSL, so direct MCP position
|
|
334
|
+
edits are the sanctioned path (stated plainly, never a false 'unprotected' alarm)."""
|
|
335
|
+
lines = [
|
|
336
|
+
f"status: {self.target}",
|
|
337
|
+
" position : manual",
|
|
338
|
+
" kind : manual (a wallet under the composer record system — NO graph/runtime)",
|
|
339
|
+
f" wallet : {self.wallet or 'unknown'}",
|
|
340
|
+
]
|
|
341
|
+
if self.strategy_id:
|
|
342
|
+
lines.append(f" record : backend strategyId {self.strategy_id}")
|
|
343
|
+
lines.append(" runtime : none — manual by design (no runtime is installed for a manual "
|
|
344
|
+
"strategy)")
|
|
345
|
+
# positions from the live seam (the wallet's clearinghouse state) — LOUD when unavailable.
|
|
346
|
+
if self.manual_positions is None:
|
|
347
|
+
reason = self.manual_positions_reason or "no positions source wired"
|
|
348
|
+
lines.append(f" positions : not read ({reason})")
|
|
349
|
+
elif not self.manual_positions:
|
|
350
|
+
lines.append(" positions : none open on this wallet")
|
|
351
|
+
else:
|
|
352
|
+
lines.append(f" positions : {len(self.manual_positions)} open on this wallet:")
|
|
353
|
+
for p in self.manual_positions:
|
|
354
|
+
asset = p.get("coin") or p.get("asset") or "?"
|
|
355
|
+
dex = p.get("dex")
|
|
356
|
+
book = f"@{dex}" if dex and dex != "main" else ""
|
|
357
|
+
sz = p.get("szi") if p.get("szi") is not None else p.get("size")
|
|
358
|
+
lines.append(f" - {asset}{book} size={sz}")
|
|
359
|
+
lines.append(" writes : direct MCP position edits on this wallet are the SANCTIONED "
|
|
360
|
+
"path for a manual strategy (there is no runtime to route through).")
|
|
361
|
+
lines.append(f" next : {self.next_hint}")
|
|
362
|
+
return "\n".join(lines) + "\n"
|
|
363
|
+
|
|
283
364
|
def _render_live(self) -> list[str]:
|
|
284
365
|
"""The H2 live block: scanners in strategy language, per-position protection in plain ROE,
|
|
285
366
|
and the risk gates + sizing. Unavailable degrades LOUD (CMP259), never silent."""
|
|
@@ -595,12 +676,58 @@ def _attach_live(result: StatusResult, state_dir, list_fn: ListFn,
|
|
|
595
676
|
runtime_id=ci.runtime_id)
|
|
596
677
|
|
|
597
678
|
|
|
679
|
+
def _build_manual_status(label: str, strategy: str, state_dir: Path,
|
|
680
|
+
positions_fn: Optional[PositionsFn]) -> StatusResult:
|
|
681
|
+
"""The MANUAL-kind status result (J3): read wallet + backend-record pointer from wallet.json,
|
|
682
|
+
and — when the positions seam is wired — the wallet's open positions (the live truth for a
|
|
683
|
+
manual strategy, which has no runtime). Unavailable positions are LOUD (never assumed flat)."""
|
|
684
|
+
wj_path, _layout = resolve_wallet_json(str(state_dir), strategy)
|
|
685
|
+
wallet: Optional[str] = None
|
|
686
|
+
strategy_id: Optional[str] = None
|
|
687
|
+
if wj_path is not None:
|
|
688
|
+
try:
|
|
689
|
+
rec = json.loads(wj_path.read_text(encoding="utf-8"))
|
|
690
|
+
wallet = str(rec.get("wallet")) if rec.get("wallet") else None
|
|
691
|
+
strategy_id = rec.get("strategyId")
|
|
692
|
+
except (OSError, json.JSONDecodeError):
|
|
693
|
+
pass
|
|
694
|
+
|
|
695
|
+
r = StatusResult(target=label, position=MANUAL, exists=True, kind=KIND_MANUAL,
|
|
696
|
+
strategy=strategy, wallet=wallet, strategy_id=strategy_id)
|
|
697
|
+
|
|
698
|
+
# positions via the existing clearinghouse seam (by wallet) — only when wired + a wallet is
|
|
699
|
+
# known. Default None => not read (LOUD), the composer never fans this per-wallet read out on its
|
|
700
|
+
# own; the rim / a test injects it.
|
|
701
|
+
if positions_fn is not None and wallet:
|
|
702
|
+
try:
|
|
703
|
+
res = positions_fn(wallet)
|
|
704
|
+
except Exception as e: # noqa: BLE001 — a seam fault must not crash status
|
|
705
|
+
r.manual_positions = None
|
|
706
|
+
r.manual_positions_reason = f"positions read raised ({e})"
|
|
707
|
+
else:
|
|
708
|
+
if res.verified:
|
|
709
|
+
r.manual_positions = list(res.positions)
|
|
710
|
+
else:
|
|
711
|
+
r.manual_positions = None
|
|
712
|
+
r.manual_positions_reason = res.reason or "positions unverifiable"
|
|
713
|
+
elif not wallet:
|
|
714
|
+
r.manual_positions_reason = "no wallet recorded in wallet.json"
|
|
715
|
+
return r
|
|
716
|
+
|
|
717
|
+
|
|
598
718
|
def run_status(target, out_dir=None, *, state_dir=None,
|
|
599
719
|
list_fn: ListFn = _box.default_list_runtimes,
|
|
600
|
-
state_fn: SystemStateFn = _live.default_system_state
|
|
720
|
+
state_fn: SystemStateFn = _live.default_system_state,
|
|
721
|
+
positions_fn: Optional[PositionsFn] = None) -> StatusResult:
|
|
601
722
|
text, label, staged_dir, strategy = _resolve(target, out_dir, state_dir)
|
|
602
723
|
|
|
603
724
|
if text is None:
|
|
725
|
+
# J3: a MANUAL strategy has NO graph (text is None) but a canonical wallet.json marked
|
|
726
|
+
# ``kind: manual``. Render its wallet-only view rather than the ABSENT "run `composer new`"
|
|
727
|
+
# (a manual strategy is legitimately graph-less). Needs the state root to find the marker.
|
|
728
|
+
if state_dir is not None and staged_dir is None \
|
|
729
|
+
and read_strategy_kind(state_dir, strategy) == KIND_MANUAL:
|
|
730
|
+
return _build_manual_status(label, strategy, Path(state_dir), positions_fn)
|
|
604
731
|
# A bare strategy name / graph that does not exist yet, but the box has state for it: still
|
|
605
732
|
# emit the install chain + path map so the operator can see what is on the box (G8b).
|
|
606
733
|
if state_dir is not None and staged_dir is None:
|
|
@@ -437,7 +437,13 @@ def run_update(
|
|
|
437
437
|
Location(),
|
|
438
438
|
), strategy=strategy, wallet=wallet)
|
|
439
439
|
|
|
440
|
-
# (2) FIND the runtime(s) bound to this wallet.
|
|
440
|
+
# (2) FIND the runtime(s) bound to this wallet. The discovery read may come from a rim-served
|
|
441
|
+
# snapshot (J9); the post-delete CONFIRM below must re-read FRESH — a snapshot predates the delete
|
|
442
|
+
# and would falsely see the just-deleted runtime as still live (CMP245). Only the PRODUCTION
|
|
443
|
+
# default seam is redirected to a fresh shell; an INJECTED seam (tests) serves both unchanged.
|
|
444
|
+
confirm_list_fn: ListFn = (
|
|
445
|
+
_box.list_runtimes_fresh if list_fn is _box.default_list_runtimes else list_fn
|
|
446
|
+
)
|
|
441
447
|
runtimes = list_fn()
|
|
442
448
|
matches = [r for r in runtimes if wallet_match(r.get("wallet"), wallet)]
|
|
443
449
|
|
|
@@ -585,9 +591,9 @@ def run_update(
|
|
|
585
591
|
Location(),
|
|
586
592
|
)],
|
|
587
593
|
message="old runtime delete failed; strategy unchanged.", **base_kw)
|
|
588
|
-
# CONFIRM gone (mirror close.py's invariant confirm).
|
|
594
|
+
# CONFIRM gone (mirror close.py's invariant confirm) — FRESH read, never a rim snapshot (J9).
|
|
589
595
|
if any(wallet_match(r.get("wallet"), wallet) and r.get("name") == old_runtime_id
|
|
590
|
-
for r in
|
|
596
|
+
for r in confirm_list_fn()):
|
|
591
597
|
return UpdateResult(
|
|
592
598
|
target=target, verdict=FAILED,
|
|
593
599
|
errors=[TeachingError(
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* that reason. Keeping every export in a single file (this one) avoids
|
|
12
12
|
* the loader's relative-import path entirely.
|
|
13
13
|
*/
|
|
14
|
+
import { type GatewayToolDefinition } from "./cli/senpi-strategy-tool.js";
|
|
14
15
|
import type { RuntimeSystemState } from "./health/index.js";
|
|
15
16
|
import type { SenpiEventBus } from "./dsl/events/index.js";
|
|
16
17
|
import type { IntakeContext } from "./runtime-api/intake/intake-context.js";
|
|
@@ -131,13 +132,14 @@ interface PluginApi {
|
|
|
131
132
|
}) => void, opts?: {
|
|
132
133
|
commands: string[];
|
|
133
134
|
}): void;
|
|
135
|
+
registerTool?(tool: GatewayToolDefinition): void;
|
|
134
136
|
registerGatewayMethod(name: string, handler: (ctx: {
|
|
135
137
|
respond: (ok: boolean, value: unknown) => void;
|
|
136
138
|
params?: Record<string, unknown>;
|
|
137
139
|
}) => void | Promise<void>, opts?: {
|
|
138
140
|
scope?: OperatorScope;
|
|
139
141
|
}): void;
|
|
140
|
-
on(hookName: string, handler: (...args: unknown[]) =>
|
|
142
|
+
on(hookName: string, handler: (...args: unknown[]) => unknown, opts?: {
|
|
141
143
|
priority?: number;
|
|
142
144
|
}): void;
|
|
143
145
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { SenpiClient } from "./senpi/client.js";
|
|
|
17
17
|
import { AuditQueryError } from "./runtime/audit-query.types.js";
|
|
18
18
|
import { buildRuntimeStateDir, archiveStateDir } from "./runtime/state-layout.js";
|
|
19
19
|
import { registerSenpiCli } from "./cli/senpi-commands.js";
|
|
20
|
+
import { createSenpiStrategyTool } from "./cli/senpi-strategy-tool.js";
|
|
20
21
|
import { readSenpiCliStore, resolveSenpiApiKeyFromStore, resolveSenpiBaseStateDir, resolveTelegramChatIdFromStore, } from "./cli/senpi-cli-config.js";
|
|
21
22
|
import { getDefaultStateDir } from "./runtime/default-state-dir.js";
|
|
22
23
|
import { createNotificationService } from "./runtime/notifications.js";
|
|
@@ -34,6 +35,7 @@ import { VERSION } from "./version.js";
|
|
|
34
35
|
import { resolvePackageName } from "./runtime/auto-update/resolve-package-name.js";
|
|
35
36
|
import { runOpenClawPluginsUpdate } from "./runtime/auto-update/run-update-command.js";
|
|
36
37
|
import { registerSpanHooks } from "./telemetry/spans/index.js";
|
|
38
|
+
import { registerComposerWriteAnnotationHook } from "./runtime/composer-managed-write.js";
|
|
37
39
|
import { initDefaultPiiRedactor } from "./telemetry/spans/redact-pii.js";
|
|
38
40
|
import { initOtelSdk, shutdownOtelSdk } from "./telemetry/otel-sdk.js";
|
|
39
41
|
import { getLogLevel, getLogLevelSource, parseLogLevel, setErrorNotifier, setLogLevel, } from "./utils/logger.js";
|
|
@@ -318,6 +320,19 @@ const senpiRuntimePlugin = {
|
|
|
318
320
|
err: err instanceof Error ? err.message : String(err),
|
|
319
321
|
});
|
|
320
322
|
}
|
|
323
|
+
// J4 — composer-managed position-write annotation. Point the "side door"
|
|
324
|
+
// (direct backend MCP position/protection edits) back at the sanctioned
|
|
325
|
+
// record path (graph spec.protection → composer check → composer update).
|
|
326
|
+
// Pointer-only: annotates the result, never blocks or damages the data path.
|
|
327
|
+
try {
|
|
328
|
+
registerComposerWriteAnnotationHook({ api, baseStateDir });
|
|
329
|
+
pluginLogger.info("[senpi-runtime] composer-managed write annotation hooks registered");
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
pluginLogger.warn("[senpi-runtime] failed to register composer-managed write annotation hooks", {
|
|
333
|
+
err: err instanceof Error ? err.message : String(err),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
321
336
|
const runtimeHandles = new Map();
|
|
322
337
|
let apiHandle;
|
|
323
338
|
// Supervised scanner state — module-scoped so stop() can reach them.
|
|
@@ -714,6 +729,13 @@ const senpiRuntimePlugin = {
|
|
|
714
729
|
pluginLogger.info("[senpi-runtime] CLI registrar callback invoked — attaching senpi subcommands to program");
|
|
715
730
|
registerSenpiCli(ctx.program, baseStateDir);
|
|
716
731
|
}, { commands: ["senpi"] });
|
|
732
|
+
if (typeof api.registerTool === "function") {
|
|
733
|
+
pluginLogger.info("[senpi-runtime] registering senpi_strategy gateway tool");
|
|
734
|
+
api.registerTool(createSenpiStrategyTool(baseStateDir));
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
pluginLogger.warn("[senpi-runtime] api.registerTool unavailable on this openclaw version — senpi_strategy gateway tool NOT registered; strategy routing falls back to the exec CLI");
|
|
738
|
+
}
|
|
717
739
|
/** Gateway respond accepts (ok, payload?, error?) with error as { code, message }; plugin SDK types may omit the third arg. */
|
|
718
740
|
api.registerGatewayMethod("senpi.installRuntime", async (ctx) => {
|
|
719
741
|
const { params } = ctx;
|