@reefclaw/openclaw-plugin 0.1.25 → 0.1.27

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/paper-adapter.js CHANGED
@@ -25,17 +25,25 @@ export class PaperAdapter {
25
25
  async closePosition(symbol, closeReason) {
26
26
  return this.simulator.closePosition(symbol, closeReason);
27
27
  }
28
+ // Reads pull a newer state.json first (ExchangeSimulator.refreshState —
29
+ // no-op without a hook). Out-of-tool readers (DB-vs-exchange sweep,
30
+ // stop-watcher) otherwise see a stale book in the process that did not
31
+ // execute the entry and post false synthetic closes / phantom stop fills.
28
32
  async getBalance() {
33
+ this.simulator.refreshState();
29
34
  return this.simulator.getBalance();
30
35
  }
31
36
  async getPositions(symbol) {
37
+ this.simulator.refreshState();
32
38
  return this.simulator.getPositions(symbol);
33
39
  }
34
40
  /** Paper has no exchange fetch to fail — position state is always known. */
35
41
  async getPositionsOrNull(symbol) {
42
+ this.simulator.refreshState();
36
43
  return this.simulator.getPositions(symbol);
37
44
  }
38
45
  async getOpenOrders(symbol) {
46
+ this.simulator.refreshState();
39
47
  return this.simulator.getOpenOrders(symbol);
40
48
  }
41
49
  async fetchOrder(orderId, _symbol) {
package/signals/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Re-export shim for the plugin-side strategy evaluator (facts-out).
2
2
  //
3
- // The generated eval-core copies (registry, strategy-adapter, direction/entry/
3
+ // The generated evaluator-core copies (registry, strategy-adapter, direction/entry/
4
4
  // stop-rules) import their types from `./types.js`. The canonical definitions
5
5
  // live in @reefclaw/shared (type-only → erased at runtime), so this shim makes
6
6
  // those relative imports resolve inside the plugin tree without pulling any
@@ -63,6 +63,13 @@ export declare class ExchangeSimulator extends EventEmitter {
63
63
  * on open and is released on close, so we must add it back here. */
64
64
  computeEquity(): number;
65
65
  private getQuoteCurrency;
66
+ private stateRefresher;
67
+ /** Install the "reload state.json if it changed" hook (index.ts reloadState). */
68
+ setStateRefresher(fn: (() => void) | null): void;
69
+ /** Pull a newer on-disk snapshot before an out-of-tool read. No-op when no
70
+ * hook is installed (tests / single-process). Never throws — a failed
71
+ * reload leaves the current in-memory book in place. */
72
+ refreshState(): void;
66
73
  getBalance(): CcxtBalance;
67
74
  getPositions(symbol?: string): CcxtPosition[];
68
75
  getOpenOrders(symbol?: string): CcxtOrder[];
@@ -153,6 +153,36 @@ export class ExchangeSimulator extends EventEmitter {
153
153
  getQuoteCurrency() {
154
154
  return this.state.config?.quoteCurrency ?? 'USDT';
155
155
  }
156
+ // ---- Cross-process refresh (two-process architecture) ----
157
+ //
158
+ // The agent and gateway processes each hold their own ExchangeSimulator and
159
+ // share state through state.json. Every paper TOOL path calls index.ts
160
+ // reloadState() before reading, but out-of-tool readers (the periodic
161
+ // DB-vs-exchange sweep, the stop-watcher's 3s poll) read the adapter
162
+ // directly — and a process that did NOT execute the entry then sees a
163
+ // stale in-memory book. On 2026-09-14 that posted a synthetic
164
+ // `reconciler_observed_flat` close 5–100s after EVERY entry on a paying
165
+ // tenant's box while the engine kept the position alive for hours. The
166
+ // hook lives on the simulator (not the adapter) so every PaperAdapter ever
167
+ // built from it — boot or any reconnect path — inherits it.
168
+ stateRefresher = null;
169
+ /** Install the "reload state.json if it changed" hook (index.ts reloadState). */
170
+ setStateRefresher(fn) {
171
+ this.stateRefresher = fn;
172
+ }
173
+ /** Pull a newer on-disk snapshot before an out-of-tool read. No-op when no
174
+ * hook is installed (tests / single-process). Never throws — a failed
175
+ * reload leaves the current in-memory book in place. */
176
+ refreshState() {
177
+ if (!this.stateRefresher)
178
+ return;
179
+ try {
180
+ this.stateRefresher();
181
+ }
182
+ catch (err) {
183
+ logger.warn(TAG, `state refresh failed: ${err instanceof Error ? err.message : String(err)}`);
184
+ }
185
+ }
156
186
  // ---- Read operations (for tools) ----
157
187
  getBalance() {
158
188
  const free = {};
@@ -150,7 +150,7 @@ export interface PositionMetadata {
150
150
  /** The agent's stated profit-realization plan, pinned at entry (indication,
151
151
  * not an enforced mechanic). */
152
152
  realizationRule?: RealizationRule;
153
- /** Re-entry cooldown gate eval, present ONLY when the gate TRIGGERED on this
153
+ /** Re-entry cooldown gate evaluation, present ONLY when the gate TRIGGERED on this
154
154
  * entry (a same-symbol loss within the window) and the order fired anyway
155
155
  * (shadow/observe). Journaled to position_entries.metadata.reentry_cooldown
156
156
  * so the shadow soak can measure the would-block cohort's forward outcomes
@@ -1113,7 +1113,7 @@ export async function createOrderTool(args, deps) {
1113
1113
  const n = num(v);
1114
1114
  return n != null ? Math.max(min, Math.min(max, n)) : undefined;
1115
1115
  };
1116
- // Journal-tag a TRIGGERED cooldown eval (shadow/observe fire-anyway cohort —
1116
+ // Journal-tag a TRIGGERED cooldown evaluation (shadow/observe fire-anyway cohort —
1117
1117
  // the measurement rows the promote-to-enforce decision reads).
1118
1118
  const reentryCooldownTag = reentryCooldownEval?.triggered
1119
1119
  ? {
@@ -22,8 +22,8 @@
22
22
  // Mode semantics mirror the exit gate (docs/CLAUDE/exit-gate.md):
23
23
  // off — not evaluated; byte-identical to the pre-gate path.
24
24
  // shadow — evaluated + logged + journal-tagged; never affects the order.
25
- // observe — as shadow, but a triggered eval logs at WARN (operator-visible).
26
- // enforce — a triggered eval hard-rejects create_order with a recovery hint.
25
+ // observe — as shadow, but a triggered evaluation logs at WARN (operator-visible).
26
+ // enforce — a triggered evaluation hard-rejects create_order with a recovery hint.
27
27
  export function evaluateReentryCooldown(inputs) {
28
28
  const base = {
29
29
  mode: inputs.mode,