context-mode 1.0.104 → 1.0.106
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +2 -31
- package/build/executor.d.ts +1 -1
- package/build/executor.js +24 -11
- package/build/opencode-plugin.d.ts +23 -6
- package/build/opencode-plugin.js +72 -30
- package/build/server.d.ts +7 -2
- package/build/server.js +61 -34
- package/build/session/db.d.ts +23 -0
- package/build/session/db.js +45 -0
- package/cli.bundle.mjs +148 -139
- package/hooks/core/routing.mjs +3 -2
- package/hooks/session-db.bundle.mjs +13 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +136 -127
- package/skills/ctx-doctor/SKILL.md +3 -3
package/build/session/db.d.ts
CHANGED
|
@@ -148,6 +148,29 @@ export declare class SessionDB extends SQLiteBase {
|
|
|
148
148
|
* Mark the resume snapshot as consumed (already injected into conversation).
|
|
149
149
|
*/
|
|
150
150
|
markResumeConsumed(sessionId: string): void;
|
|
151
|
+
/**
|
|
152
|
+
* Atomically claim the most recent unconsumed resume snapshot in this DB,
|
|
153
|
+
* EXCLUDING any row that belongs to `currentSessionId`.
|
|
154
|
+
*
|
|
155
|
+
* `SessionDB` is sharded per project (see `getSessionDBPath` — SHA-256 of
|
|
156
|
+
* project dir), so "this DB" already implies "this project". The atomic
|
|
157
|
+
* `UPDATE … RETURNING` ensures concurrent processes for the same project
|
|
158
|
+
* cannot both inject the same snapshot (Mickey / PR #376 race).
|
|
159
|
+
*
|
|
160
|
+
* The `currentSessionId` parameter prevents self-injection: when a session
|
|
161
|
+
* compacts mid-flight and produces its own row, that session's next chat
|
|
162
|
+
* turn must NOT claim that row back (wasted tokens AND it would consume
|
|
163
|
+
* the snapshot meant for the next fresh session).
|
|
164
|
+
*
|
|
165
|
+
* Pass an empty string to allow self-claim (legacy behaviour, only useful
|
|
166
|
+
* in tests or one-off harnesses).
|
|
167
|
+
*
|
|
168
|
+
* Returns null when no unconsumed snapshot exists for any other session.
|
|
169
|
+
*/
|
|
170
|
+
claimLatestUnconsumedResume(currentSessionId: string): {
|
|
171
|
+
sessionId: string;
|
|
172
|
+
snapshot: string;
|
|
173
|
+
} | null;
|
|
151
174
|
/**
|
|
152
175
|
* Return the most recent session_id from session_meta, or null if none.
|
|
153
176
|
* Used by the runtime to attach persistent counters to the right session
|
package/build/session/db.js
CHANGED
|
@@ -87,6 +87,7 @@ const S = {
|
|
|
87
87
|
upsertResume: "upsertResume",
|
|
88
88
|
getResume: "getResume",
|
|
89
89
|
markResumeConsumed: "markResumeConsumed",
|
|
90
|
+
claimLatestUnconsumedResume: "claimLatestUnconsumedResume",
|
|
90
91
|
deleteEvents: "deleteEvents",
|
|
91
92
|
deleteMeta: "deleteMeta",
|
|
92
93
|
deleteResume: "deleteResume",
|
|
@@ -251,6 +252,25 @@ export class SessionDB extends SQLiteBase {
|
|
|
251
252
|
consumed = 0`);
|
|
252
253
|
p(S.getResume, `SELECT snapshot, event_count, consumed FROM session_resume WHERE session_id = ?`);
|
|
253
254
|
p(S.markResumeConsumed, `UPDATE session_resume SET consumed = 1 WHERE session_id = ?`);
|
|
255
|
+
// Atomic "pick newest unconsumed snapshot AND mark it consumed in one
|
|
256
|
+
// statement". Required for race-safe cross-session resume injection
|
|
257
|
+
// (Mickey / PR #376) — two parallel chat-turn hooks must not both read
|
|
258
|
+
// the same row before either one writes consumed=1.
|
|
259
|
+
//
|
|
260
|
+
// The `session_id != ?` clause prevents self-injection (v1.0.106): when
|
|
261
|
+
// Session B compacts mid-flight and produces its own row, B's next chat
|
|
262
|
+
// turn must NOT claim that row back into its own prompt — that's wasted
|
|
263
|
+
// tokens and steals the snapshot meant for the next fresh session.
|
|
264
|
+
p(S.claimLatestUnconsumedResume, `UPDATE session_resume
|
|
265
|
+
SET consumed = 1
|
|
266
|
+
WHERE id = (
|
|
267
|
+
SELECT id FROM session_resume
|
|
268
|
+
WHERE consumed = 0
|
|
269
|
+
AND session_id != ?
|
|
270
|
+
ORDER BY created_at DESC, id DESC
|
|
271
|
+
LIMIT 1
|
|
272
|
+
)
|
|
273
|
+
RETURNING session_id, snapshot`);
|
|
254
274
|
// ── Delete ──
|
|
255
275
|
p(S.deleteEvents, `DELETE FROM session_events WHERE session_id = ?`);
|
|
256
276
|
p(S.deleteMeta, `DELETE FROM session_meta WHERE session_id = ?`);
|
|
@@ -478,6 +498,31 @@ export class SessionDB extends SQLiteBase {
|
|
|
478
498
|
markResumeConsumed(sessionId) {
|
|
479
499
|
this.stmt(S.markResumeConsumed).run(sessionId);
|
|
480
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* Atomically claim the most recent unconsumed resume snapshot in this DB,
|
|
503
|
+
* EXCLUDING any row that belongs to `currentSessionId`.
|
|
504
|
+
*
|
|
505
|
+
* `SessionDB` is sharded per project (see `getSessionDBPath` — SHA-256 of
|
|
506
|
+
* project dir), so "this DB" already implies "this project". The atomic
|
|
507
|
+
* `UPDATE … RETURNING` ensures concurrent processes for the same project
|
|
508
|
+
* cannot both inject the same snapshot (Mickey / PR #376 race).
|
|
509
|
+
*
|
|
510
|
+
* The `currentSessionId` parameter prevents self-injection: when a session
|
|
511
|
+
* compacts mid-flight and produces its own row, that session's next chat
|
|
512
|
+
* turn must NOT claim that row back (wasted tokens AND it would consume
|
|
513
|
+
* the snapshot meant for the next fresh session).
|
|
514
|
+
*
|
|
515
|
+
* Pass an empty string to allow self-claim (legacy behaviour, only useful
|
|
516
|
+
* in tests or one-off harnesses).
|
|
517
|
+
*
|
|
518
|
+
* Returns null when no unconsumed snapshot exists for any other session.
|
|
519
|
+
*/
|
|
520
|
+
claimLatestUnconsumedResume(currentSessionId) {
|
|
521
|
+
const row = this.stmt(S.claimLatestUnconsumedResume).get(currentSessionId);
|
|
522
|
+
if (!row)
|
|
523
|
+
return null;
|
|
524
|
+
return { sessionId: row.session_id, snapshot: row.snapshot };
|
|
525
|
+
}
|
|
481
526
|
/**
|
|
482
527
|
* Return the most recent session_id from session_meta, or null if none.
|
|
483
528
|
* Used by the runtime to attach persistent counters to the right session
|