@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.
- package/dist/context-dedup.d.ts +25 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +22 -6
- package/package.json +1 -1
|
@@ -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:
|
|
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.
|
|
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
|
-
|
|
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:
|
|
4415
|
+
configSchema: { type: "object", properties: {}, additionalProperties: true },
|
|
4400
4416
|
register(api) {
|
|
4401
4417
|
setRuntime(api.runtime);
|
|
4402
4418
|
api.registerChannel({ plugin: occPlugin });
|