backeer-opencode 0.2.4 → 0.2.5
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/CHANGELOG.md +7 -0
- package/dist/backeer-permission.js +44 -20
- package/package.json +1 -1
- package/src/feedback-session-store.js +26 -14
- package/src/session-messages.js +18 -4
- package/src/updates.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `backeer-opencode` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.2.5 - 2026-09-01
|
|
6
|
+
|
|
7
|
+
- Group consecutive OpenCode conversation feedback into one ignored Backeer
|
|
8
|
+
message while preserving permission and question UI blocking.
|
|
9
|
+
- Render feedback with plain terminal-friendly text so OpenCode never exposes
|
|
10
|
+
raw Markdown markers such as `**Backeer**`.
|
|
11
|
+
|
|
5
12
|
## 0.2.4 - 2026-09-01
|
|
6
13
|
|
|
7
14
|
- Retry permission, question, and todo publications after a terminal delivery
|
|
@@ -468,7 +468,7 @@ import { randomUUID } from "node:crypto";
|
|
|
468
468
|
import { statSync } from "node:fs";
|
|
469
469
|
|
|
470
470
|
// src/updates.js
|
|
471
|
-
var PLUGIN_VERSION = "0.2.
|
|
471
|
+
var PLUGIN_VERSION = "0.2.5";
|
|
472
472
|
var PACKAGE_NAME = "backeer-opencode";
|
|
473
473
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
474
474
|
var CHECK_TIMEOUT_MS = 3e3;
|
|
@@ -709,9 +709,10 @@ async function publishTestNotification(config, reporter) {
|
|
|
709
709
|
// src/feedback-session-store.js
|
|
710
710
|
var MAX_QUEUED_MESSAGES = 20;
|
|
711
711
|
var FeedbackSessionStore = class {
|
|
712
|
-
constructor({ send, reporter }) {
|
|
712
|
+
constructor({ send, reporter, flushDelayMs = 150 }) {
|
|
713
713
|
this.send = send;
|
|
714
714
|
this.reporter = reporter;
|
|
715
|
+
this.flushDelayMs = flushDelayMs;
|
|
715
716
|
this.sessions = /* @__PURE__ */ new Map();
|
|
716
717
|
this.disposed = false;
|
|
717
718
|
}
|
|
@@ -724,7 +725,8 @@ var FeedbackSessionStore = class {
|
|
|
724
725
|
blockers: /* @__PURE__ */ new Set(),
|
|
725
726
|
queue: [],
|
|
726
727
|
queued: /* @__PURE__ */ new Set(),
|
|
727
|
-
queueOverflowReported: false
|
|
728
|
+
queueOverflowReported: false,
|
|
729
|
+
flushTimer: void 0
|
|
728
730
|
};
|
|
729
731
|
rememberBounded(this.sessions, sessionID, state);
|
|
730
732
|
}
|
|
@@ -733,23 +735,27 @@ var FeedbackSessionStore = class {
|
|
|
733
735
|
hasReadyMessage(state) {
|
|
734
736
|
return !this.disposed && state.idle && state.blockers.size === 0 && state.queue.length > 0;
|
|
735
737
|
}
|
|
738
|
+
scheduleFlush(sessionID, state) {
|
|
739
|
+
if (state.flushTimer || !this.hasReadyMessage(state)) return;
|
|
740
|
+
state.flushTimer = setTimeout(() => {
|
|
741
|
+
state.flushTimer = void 0;
|
|
742
|
+
void this.flush(sessionID, state);
|
|
743
|
+
}, this.flushDelayMs);
|
|
744
|
+
}
|
|
736
745
|
async flush(sessionID, state) {
|
|
737
746
|
if (state.flushing || !this.hasReadyMessage(state)) return;
|
|
738
747
|
state.flushing = true;
|
|
748
|
+
const items = state.queue.slice();
|
|
739
749
|
try {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
} catch (error) {
|
|
747
|
-
state.idle = false;
|
|
748
|
-
this.reporter.debug("Conversation feedback could not be displayed", error);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
750
|
+
await this.send(sessionID, items);
|
|
751
|
+
state.queue.splice(0, items.length);
|
|
752
|
+
for (const item of items) state.queued.delete(item.key);
|
|
753
|
+
} catch (error) {
|
|
754
|
+
state.idle = false;
|
|
755
|
+
this.reporter.debug("Conversation feedback could not be displayed", error);
|
|
751
756
|
} finally {
|
|
752
757
|
state.flushing = false;
|
|
758
|
+
this.scheduleFlush(sessionID, state);
|
|
753
759
|
}
|
|
754
760
|
}
|
|
755
761
|
enqueue(kind, sessionID, message) {
|
|
@@ -767,13 +773,13 @@ var FeedbackSessionStore = class {
|
|
|
767
773
|
this.reporter.warning("Conversation feedback queue full \u2014 oldest message discarded");
|
|
768
774
|
}
|
|
769
775
|
}
|
|
770
|
-
|
|
776
|
+
this.scheduleFlush(sessionID, state);
|
|
771
777
|
}
|
|
772
778
|
setIdle(sessionID, idle) {
|
|
773
779
|
if (typeof sessionID !== "string" || !sessionID) return;
|
|
774
780
|
const state = this.stateFor(sessionID);
|
|
775
781
|
state.idle = idle;
|
|
776
|
-
if (idle)
|
|
782
|
+
if (idle) this.scheduleFlush(sessionID, state);
|
|
777
783
|
}
|
|
778
784
|
beginInteraction(sessionID, key) {
|
|
779
785
|
const state = this.stateFor(sessionID);
|
|
@@ -794,10 +800,15 @@ var FeedbackSessionStore = class {
|
|
|
794
800
|
state.idle = false;
|
|
795
801
|
}
|
|
796
802
|
delete(sessionID) {
|
|
803
|
+
const state = this.sessions.get(sessionID);
|
|
804
|
+
if (state?.flushTimer) clearTimeout(state.flushTimer);
|
|
797
805
|
this.sessions.delete(sessionID);
|
|
798
806
|
}
|
|
799
807
|
dispose() {
|
|
800
808
|
this.disposed = true;
|
|
809
|
+
for (const state of this.sessions.values()) {
|
|
810
|
+
if (state.flushTimer) clearTimeout(state.flushTimer);
|
|
811
|
+
}
|
|
801
812
|
this.sessions.clear();
|
|
802
813
|
}
|
|
803
814
|
};
|
|
@@ -874,15 +885,28 @@ async function sendSessionMessage(client, sessionID, text) {
|
|
|
874
885
|
}
|
|
875
886
|
function formatBackeerFeedback(kind, message) {
|
|
876
887
|
const symbol = FEEDBACK_SYMBOLS[kind] ?? FEEDBACK_SYMBOLS.info;
|
|
877
|
-
return
|
|
888
|
+
return `\u25A3 Backeer | ${symbol} ${limit(message, 300)}`;
|
|
889
|
+
}
|
|
890
|
+
function formatBackeerFeedbackBatch(items) {
|
|
891
|
+
if (items.length === 1) {
|
|
892
|
+
return formatBackeerFeedback(items[0].kind, items[0].message);
|
|
893
|
+
}
|
|
894
|
+
return [
|
|
895
|
+
"\u25A3 Backeer",
|
|
896
|
+
...items.map((item) => {
|
|
897
|
+
const symbol = FEEDBACK_SYMBOLS[item.kind] ?? FEEDBACK_SYMBOLS.info;
|
|
898
|
+
return `${symbol} ${limit(item.message, 300)}`;
|
|
899
|
+
})
|
|
900
|
+
].join("\n");
|
|
878
901
|
}
|
|
879
|
-
function createConversationFeedback({ client, reporter }) {
|
|
902
|
+
function createConversationFeedback({ client, reporter, flushDelayMs = 150 }) {
|
|
880
903
|
const sessions = new FeedbackSessionStore({
|
|
881
904
|
reporter,
|
|
882
|
-
|
|
905
|
+
flushDelayMs,
|
|
906
|
+
send: (sessionID, items) => sendSessionMessage(
|
|
883
907
|
client,
|
|
884
908
|
sessionID,
|
|
885
|
-
|
|
909
|
+
formatBackeerFeedbackBatch(items)
|
|
886
910
|
)
|
|
887
911
|
});
|
|
888
912
|
return {
|
package/package.json
CHANGED
|
@@ -3,9 +3,10 @@ import { limit, rememberBounded } from "./utilities.js"
|
|
|
3
3
|
const MAX_QUEUED_MESSAGES = 20
|
|
4
4
|
|
|
5
5
|
export class FeedbackSessionStore {
|
|
6
|
-
constructor({ send, reporter }) {
|
|
6
|
+
constructor({ send, reporter, flushDelayMs = 150 }) {
|
|
7
7
|
this.send = send
|
|
8
8
|
this.reporter = reporter
|
|
9
|
+
this.flushDelayMs = flushDelayMs
|
|
9
10
|
this.sessions = new Map()
|
|
10
11
|
this.disposed = false
|
|
11
12
|
}
|
|
@@ -20,6 +21,7 @@ export class FeedbackSessionStore {
|
|
|
20
21
|
queue: [],
|
|
21
22
|
queued: new Set(),
|
|
22
23
|
queueOverflowReported: false,
|
|
24
|
+
flushTimer: undefined,
|
|
23
25
|
}
|
|
24
26
|
rememberBounded(this.sessions, sessionID, state)
|
|
25
27
|
}
|
|
@@ -31,23 +33,28 @@ export class FeedbackSessionStore {
|
|
|
31
33
|
state.blockers.size === 0 && state.queue.length > 0
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
scheduleFlush(sessionID, state) {
|
|
37
|
+
if (state.flushTimer || !this.hasReadyMessage(state)) return
|
|
38
|
+
state.flushTimer = setTimeout(() => {
|
|
39
|
+
state.flushTimer = undefined
|
|
40
|
+
void this.flush(sessionID, state)
|
|
41
|
+
}, this.flushDelayMs)
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
async flush(sessionID, state) {
|
|
35
45
|
if (state.flushing || !this.hasReadyMessage(state)) return
|
|
36
46
|
state.flushing = true
|
|
47
|
+
const items = state.queue.slice()
|
|
37
48
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} catch (error) {
|
|
45
|
-
state.idle = false
|
|
46
|
-
this.reporter.debug("Conversation feedback could not be displayed", error)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
+
await this.send(sessionID, items)
|
|
50
|
+
state.queue.splice(0, items.length)
|
|
51
|
+
for (const item of items) state.queued.delete(item.key)
|
|
52
|
+
} catch (error) {
|
|
53
|
+
state.idle = false
|
|
54
|
+
this.reporter.debug("Conversation feedback could not be displayed", error)
|
|
49
55
|
} finally {
|
|
50
56
|
state.flushing = false
|
|
57
|
+
this.scheduleFlush(sessionID, state)
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
|
|
@@ -67,14 +74,14 @@ export class FeedbackSessionStore {
|
|
|
67
74
|
this.reporter.warning("Conversation feedback queue full — oldest message discarded")
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
|
-
|
|
77
|
+
this.scheduleFlush(sessionID, state)
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
setIdle(sessionID, idle) {
|
|
74
81
|
if (typeof sessionID !== "string" || !sessionID) return
|
|
75
82
|
const state = this.stateFor(sessionID)
|
|
76
83
|
state.idle = idle
|
|
77
|
-
if (idle)
|
|
84
|
+
if (idle) this.scheduleFlush(sessionID, state)
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
beginInteraction(sessionID, key) {
|
|
@@ -100,11 +107,16 @@ export class FeedbackSessionStore {
|
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
delete(sessionID) {
|
|
110
|
+
const state = this.sessions.get(sessionID)
|
|
111
|
+
if (state?.flushTimer) clearTimeout(state.flushTimer)
|
|
103
112
|
this.sessions.delete(sessionID)
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
dispose() {
|
|
107
116
|
this.disposed = true
|
|
117
|
+
for (const state of this.sessions.values()) {
|
|
118
|
+
if (state.flushTimer) clearTimeout(state.flushTimer)
|
|
119
|
+
}
|
|
108
120
|
this.sessions.clear()
|
|
109
121
|
}
|
|
110
122
|
}
|
package/src/session-messages.js
CHANGED
|
@@ -82,16 +82,30 @@ export async function sendSessionMessage(client, sessionID, text) {
|
|
|
82
82
|
|
|
83
83
|
export function formatBackeerFeedback(kind, message) {
|
|
84
84
|
const symbol = FEEDBACK_SYMBOLS[kind] ?? FEEDBACK_SYMBOLS.info
|
|
85
|
-
return
|
|
85
|
+
return `▣ Backeer | ${symbol} ${limit(message, 300)}`
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
export function
|
|
88
|
+
export function formatBackeerFeedbackBatch(items) {
|
|
89
|
+
if (items.length === 1) {
|
|
90
|
+
return formatBackeerFeedback(items[0].kind, items[0].message)
|
|
91
|
+
}
|
|
92
|
+
return [
|
|
93
|
+
"▣ Backeer",
|
|
94
|
+
...items.map((item) => {
|
|
95
|
+
const symbol = FEEDBACK_SYMBOLS[item.kind] ?? FEEDBACK_SYMBOLS.info
|
|
96
|
+
return `${symbol} ${limit(item.message, 300)}`
|
|
97
|
+
}),
|
|
98
|
+
].join("\n")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function createConversationFeedback({ client, reporter, flushDelayMs = 150 }) {
|
|
89
102
|
const sessions = new FeedbackSessionStore({
|
|
90
103
|
reporter,
|
|
91
|
-
|
|
104
|
+
flushDelayMs,
|
|
105
|
+
send: (sessionID, items) => sendSessionMessage(
|
|
92
106
|
client,
|
|
93
107
|
sessionID,
|
|
94
|
-
|
|
108
|
+
formatBackeerFeedbackBatch(items),
|
|
95
109
|
),
|
|
96
110
|
})
|
|
97
111
|
|
package/src/updates.js
CHANGED