@openclawcity/openclawcity 1.0.15 → 1.0.17

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.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * City-context injection de-duplication.
3
+ *
4
+ * The channel prepends a [CITY CONTEXT] heartbeat snapshot to each inbound event
5
+ * so the model knows where it is. That snapshot is cached (~5 min), so within a
6
+ * burst of events every prepend is byte-for-byte identical — re-injecting it adds
7
+ * no information, only token bloat and a wall of repeated context in the agent's
8
+ * history (the Aaga model-login loop, 2026-06-30, prepended it ~7× in a minute).
9
+ *
10
+ * This gates the prepend: inject only when it's the first event for a key, the
11
+ * window has elapsed, or the snapshot actually changed (cache refresh). Keyed per
12
+ * (account, peer) so two concurrent conversations each still receive context — only
13
+ * redundant repeats inside one conversation are suppressed. No information is lost:
14
+ * the suppressed copies are identical to the one already in the session history.
15
+ */
16
+ export interface ContextInjectionRecord {
17
+ at: number;
18
+ ctx: string;
19
+ }
20
+ /**
21
+ * Decide whether to prepend the city-context snapshot for `key`, and record the
22
+ * decision in `state`. Returns true to inject (and updates the record), false to
23
+ * skip a recent identical repeat. Pure given (state, key, ctx, now, windowMs).
24
+ */
25
+ export declare function shouldInjectCityContext(state: Map<string, ContextInjectionRecord>, key: string, ctx: string, now: number, windowMs: number): boolean;
package/dist/index.d.ts CHANGED
@@ -3,7 +3,11 @@ export declare function sanitizeReplyText(text: string): string | null;
3
3
  declare const plugin: {
4
4
  id: string;
5
5
  name: string;
6
- configSchema: Record<string, unknown>;
6
+ configSchema: {
7
+ type: "object";
8
+ properties: {};
9
+ additionalProperties: boolean;
10
+ };
7
11
  register(api: OpenClawPluginApi): void;
8
12
  };
9
13
  export default plugin;
package/dist/index.js CHANGED
@@ -3646,9 +3646,6 @@ var require_websocket_server = __commonJS({
3646
3646
  }
3647
3647
  });
3648
3648
 
3649
- // .tsc-out/index.js
3650
- import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
3651
-
3652
3649
  // .tsc-out/runtime.js
3653
3650
  var runtime = null;
3654
3651
  function setRuntime(next) {
@@ -4072,9 +4069,22 @@ var OpenClawCityAdapter = class {
4072
4069
 
4073
4070
  // .tsc-out/index.js
4074
4071
  import { exposeAccountEnv, clearAccountEnv } from "./env-bridge.js";
4072
+
4073
+ // .tsc-out/context-dedup.js
4074
+ function shouldInjectCityContext(state, key, ctx, now, windowMs) {
4075
+ const prev = state.get(key);
4076
+ const inject = !prev || now - prev.at >= windowMs || prev.ctx !== ctx;
4077
+ if (inject) {
4078
+ state.set(key, { at: now, ctx });
4079
+ }
4080
+ return inject;
4081
+ }
4082
+
4083
+ // .tsc-out/index.js
4075
4084
  var CHANNEL_ID = "openclawcity";
4076
4085
  var DEFAULT_API_BASE = "https://api.openbotcity.com";
4077
4086
  var HEARTBEAT_CACHE_MS = 5 * 60 * 1e3;
4087
+ var CONTEXT_REINJECT_WINDOW_MS = 60 * 1e3;
4078
4088
  function deriveApiBase(gatewayUrl) {
4079
4089
  if (!gatewayUrl)
4080
4090
  return DEFAULT_API_BASE;
@@ -4094,6 +4104,7 @@ function sanitizeReplyText(text) {
4094
4104
  }
4095
4105
  var adapters = /* @__PURE__ */ new Map();
4096
4106
  var heartbeatCache = /* @__PURE__ */ new Map();
4107
+ var contextInjectionState = /* @__PURE__ */ new Map();
4097
4108
  async function fetchHeartbeatContext(apiBase, jwt, accountId, log) {
4098
4109
  const cached = heartbeatCache.get(accountId);
4099
4110
  const now = Date.now();
@@ -4194,12 +4205,17 @@ var occPlugin = {
4194
4205
  const apiBase = deriveApiBase(account.gatewayUrl);
4195
4206
  const cityCtx = await fetchHeartbeatContext(apiBase, account.apiKey, accountId, log);
4196
4207
  if (cityCtx) {
4197
- envelope.content.text = `[CITY CONTEXT]
4208
+ const dedupKey = `${accountId}:${envelope.sender.id}`;
4209
+ if (shouldInjectCityContext(contextInjectionState, dedupKey, cityCtx, Date.now(), CONTEXT_REINJECT_WINDOW_MS)) {
4210
+ envelope.content.text = `[CITY CONTEXT]
4198
4211
  ${cityCtx}
4199
4212
  [/CITY CONTEXT]
4200
4213
 
4201
4214
  ${envelope.content.text}`;
4202
- log?.info?.(`[OCC] City context prepended (${cityCtx.length} bytes)`);
4215
+ log?.info?.(`[OCC] City context prepended (${cityCtx.length} bytes)`);
4216
+ } else {
4217
+ log?.info?.(`[OCC] City context skipped (recent + unchanged) for ${dedupKey}`);
4218
+ }
4203
4219
  }
4204
4220
  log?.info?.(`[OCC] Step 1: resolveAgentRoute...`);
4205
4221
  let route;
@@ -4396,7 +4412,7 @@ ${envelope.content.text}`;
4396
4412
  var plugin = {
4397
4413
  id: CHANNEL_ID,
4398
4414
  name: "OpenClawCity Channel",
4399
- configSchema: emptyPluginConfigSchema(),
4415
+ configSchema: { type: "object", properties: {}, additionalProperties: true },
4400
4416
  register(api) {
4401
4417
  setRuntime(api.runtime);
4402
4418
  api.registerChannel({ plugin: occPlugin });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclawcity/openclawcity",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "OpenClawCity channel plugin for OpenClaw — live city events for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",