opencode-wake-plugin 0.1.0

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +193 -0
  4. package/dist/contract.d.ts +13 -0
  5. package/dist/contract.d.ts.map +1 -0
  6. package/dist/contract.js +39 -0
  7. package/dist/contract.js.map +1 -0
  8. package/dist/event-filter.d.ts +11 -0
  9. package/dist/event-filter.d.ts.map +1 -0
  10. package/dist/event-filter.js +43 -0
  11. package/dist/event-filter.js.map +1 -0
  12. package/dist/index.d.ts +8 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +23 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/logger.d.ts +58 -0
  17. package/dist/logger.d.ts.map +1 -0
  18. package/dist/logger.js +157 -0
  19. package/dist/logger.js.map +1 -0
  20. package/dist/omo-detector.d.ts +14 -0
  21. package/dist/omo-detector.d.ts.map +1 -0
  22. package/dist/omo-detector.js +74 -0
  23. package/dist/omo-detector.js.map +1 -0
  24. package/dist/server.d.ts +4 -0
  25. package/dist/server.d.ts.map +1 -0
  26. package/dist/server.js +126 -0
  27. package/dist/server.js.map +1 -0
  28. package/dist/tui.d.ts +9 -0
  29. package/dist/tui.d.ts.map +1 -0
  30. package/dist/tui.js +32 -0
  31. package/dist/tui.js.map +1 -0
  32. package/dist/wake-coalescer.d.ts +15 -0
  33. package/dist/wake-coalescer.d.ts.map +1 -0
  34. package/dist/wake-coalescer.js +43 -0
  35. package/dist/wake-coalescer.js.map +1 -0
  36. package/dist/wake-dispatcher.d.ts +68 -0
  37. package/dist/wake-dispatcher.d.ts.map +1 -0
  38. package/dist/wake-dispatcher.js +126 -0
  39. package/dist/wake-dispatcher.js.map +1 -0
  40. package/package.json +73 -0
  41. package/src/contract.ts +48 -0
  42. package/src/event-filter.ts +57 -0
  43. package/src/index.ts +25 -0
  44. package/src/logger.ts +205 -0
  45. package/src/omo-detector.ts +82 -0
  46. package/src/server.ts +142 -0
  47. package/src/tui.ts +37 -0
  48. package/src/wake-coalescer.ts +54 -0
  49. package/src/wake-dispatcher.ts +167 -0
@@ -0,0 +1,14 @@
1
+ export type OmoFixStatus = "fully-fixed" | "partial-or-older" | "unknown";
2
+ /**
3
+ * Returns true iff the supplied version string is a fully-fixed release
4
+ * (>= 5.0.0). Any non-parseable, undefined, null, or empty input returns false.
5
+ */
6
+ export declare function isOmoFullyFixed(version: string | undefined | null): boolean;
7
+ /**
8
+ * Classifies an OMO version into a UI-/policy-friendly status string.
9
+ * - "fully-fixed" : >= 5.0.0 (plugin may self-disable)
10
+ * - "partial-or-older": parseable but below 5.0.0 (plugin stays active)
11
+ * - "unknown" : undefined / null / empty / non-semver (plugin stays active)
12
+ */
13
+ export declare function classifyOmoVersion(version: string | undefined | null): OmoFixStatus;
14
+ //# sourceMappingURL=omo-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"omo-detector.d.ts","sourceRoot":"","sources":["../src/omo-detector.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,kBAAkB,GAAG,SAAS,CAAC;AAyC1E;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAa3E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,YAAY,CAMnF"}
@@ -0,0 +1,74 @@
1
+ // omo-detector
2
+ //
3
+ // Classifies the currently-installed oh-my-openagent version to decide whether
4
+ // the wake plugin needs to stay active.
5
+ //
6
+ // Why: post PR #5601 in OMO 4.13.0+, the deferred-wake cascade has a partial
7
+ // fix. We self-disable only when a hypothetical 5.0.0+ ships a fully-reliable
8
+ // retry path; until then we stay defensive.
9
+ const FULLY_FIXED_MAJOR = 5;
10
+ const FULLY_FIXED_MINOR = 0;
11
+ const FULLY_FIXED_PATCH = 0;
12
+ const SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
13
+ /** Parses a strict "MAJOR.MINOR.PATCH" string. Returns null on any deviation. */
14
+ function parseSemver(version) {
15
+ if (version === undefined || version === null || version === "") {
16
+ return null;
17
+ }
18
+ const match = SEMVER_PATTERN.exec(version);
19
+ if (match === null) {
20
+ return null;
21
+ }
22
+ // Indices are guaranteed by the regex anchors; under noUncheckedIndexedAccess
23
+ // we still need narrow undefined checks.
24
+ const majorRaw = match[1];
25
+ const minorRaw = match[2];
26
+ const patchRaw = match[3];
27
+ if (majorRaw === undefined || minorRaw === undefined || patchRaw === undefined) {
28
+ return null;
29
+ }
30
+ const major = Number.parseInt(majorRaw, 10);
31
+ const minor = Number.parseInt(minorRaw, 10);
32
+ const patch = Number.parseInt(patchRaw, 10);
33
+ if (!Number.isFinite(major) ||
34
+ !Number.isFinite(minor) ||
35
+ !Number.isFinite(patch) ||
36
+ major < 0 ||
37
+ minor < 0 ||
38
+ patch < 0) {
39
+ return null;
40
+ }
41
+ return [major, minor, patch];
42
+ }
43
+ /**
44
+ * Returns true iff the supplied version string is a fully-fixed release
45
+ * (>= 5.0.0). Any non-parseable, undefined, null, or empty input returns false.
46
+ */
47
+ export function isOmoFullyFixed(version) {
48
+ const parsed = parseSemver(version);
49
+ if (parsed === null) {
50
+ return false;
51
+ }
52
+ const [major, minor, patch] = parsed;
53
+ if (major !== FULLY_FIXED_MAJOR) {
54
+ return major > FULLY_FIXED_MAJOR;
55
+ }
56
+ if (minor !== FULLY_FIXED_MINOR) {
57
+ return minor > FULLY_FIXED_MINOR;
58
+ }
59
+ return patch >= FULLY_FIXED_PATCH;
60
+ }
61
+ /**
62
+ * Classifies an OMO version into a UI-/policy-friendly status string.
63
+ * - "fully-fixed" : >= 5.0.0 (plugin may self-disable)
64
+ * - "partial-or-older": parseable but below 5.0.0 (plugin stays active)
65
+ * - "unknown" : undefined / null / empty / non-semver (plugin stays active)
66
+ */
67
+ export function classifyOmoVersion(version) {
68
+ const parsed = parseSemver(version);
69
+ if (parsed === null) {
70
+ return "unknown";
71
+ }
72
+ return isOmoFullyFixed(version) ? "fully-fixed" : "partial-or-older";
73
+ }
74
+ //# sourceMappingURL=omo-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"omo-detector.js","sourceRoot":"","sources":["../src/omo-detector.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,EAAE;AACF,+EAA+E;AAC/E,wCAAwC;AACxC,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,4CAA4C;AAI5C,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,MAAM,cAAc,GAAG,uBAAuB,CAAC;AAE/C,iFAAiF;AACjF,SAAS,WAAW,CAAC,OAAkC;IACrD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,8EAA8E;IAC9E,yCAAyC;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5C,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACvB,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC,EACT,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAkC;IAChE,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC;IACrC,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;QAChC,OAAO,KAAK,GAAG,iBAAiB,CAAC;IACnC,CAAC;IACD,IAAI,KAAK,KAAK,iBAAiB,EAAE,CAAC;QAChC,OAAO,KAAK,GAAG,iBAAiB,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,IAAI,iBAAiB,CAAC;AACpC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAkC;IACnE,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,kBAAkB,CAAC;AACvE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const WakePlugin: Plugin;
3
+ export default WakePlugin;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AA+D/D,eAAO,MAAM,UAAU,EAAE,MA+DxB,CAAC;AAEF,eAAe,UAAU,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,126 @@
1
+ // opencode-wake-plugin
2
+ //
3
+ // Plugin entry point. Wires the opencode `event` hook, the
4
+ // WakeCoalescer (debounce), and the WakeDispatcher (retry with backoff)
5
+ // into a single reaction chain:
6
+ //
7
+ // session.idle → parent lookup → coalesce per parent → dispatch wake
8
+ //
9
+ // Self-disables cleanly when the host reports an oh-my-openagent version
10
+ // >= 5.0.0 (hypothetical "fully fixed" OMO; the user's installed 4.15.1
11
+ // stays active and provides belt-and-suspenders coverage for the partial
12
+ // PR #5601 retry path).
13
+ import { shouldWakeOn } from "./event-filter.js";
14
+ import { WakeCoalescer } from "./wake-coalescer.js";
15
+ import { WakeDispatcher } from "./wake-dispatcher.js";
16
+ import { classifyOmoVersion } from "./omo-detector.js";
17
+ import { logger } from "./logger.js";
18
+ const WAKE_DEBOUNCE_MS = 5500;
19
+ const WAKE_BACKOFF_MS = [1000, 3000, 9000];
20
+ /**
21
+ * Look up the parent of a session that just emitted `session.idle`.
22
+ * Returns null for the root session (no parent to wake).
23
+ *
24
+ * Uses `client.session.get()` since the SDK does NOT include `parentID`
25
+ * in the `EventSessionIdle` payload properties.
26
+ */
27
+ async function lookupParentSessionID(client, sessionID) {
28
+ logger.debug("lookup parent", { sessionID });
29
+ try {
30
+ // SDK uses `{ path: { id } }` (not sessionID), per SessionGetData.path.
31
+ const res = (await client.session.get({ path: { id: sessionID } }));
32
+ const parentID = res.data?.parentID;
33
+ if (typeof parentID === "string" && parentID.length > 0) {
34
+ logger.debug("parent resolved", { sessionID, parentID });
35
+ return parentID;
36
+ }
37
+ return null;
38
+ }
39
+ catch (error) {
40
+ logger.debug("parent lookup threw", {
41
+ sessionID,
42
+ error: error instanceof Error ? error.message : String(error),
43
+ });
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Probe the host's enabled plugin list for an oh-my-openagent entry
49
+ * (e.g. `"oh-my-openagent@4.15.1"` or `"oh-my-openagent@latest"`).
50
+ * Returns the parsed version when possible, else "unknown".
51
+ */
52
+ function probeOmoVersionFromPluginList(plugins) {
53
+ if (!Array.isArray(plugins))
54
+ return "unknown";
55
+ for (const entry of plugins) {
56
+ const raw = typeof entry === "string" ? entry : Array.isArray(entry) ? entry[0] : null;
57
+ if (typeof raw !== "string")
58
+ continue;
59
+ const match = /^oh-my-openagent@(\d+\.\d+\.\d+)$/.exec(raw);
60
+ if (match && match[1])
61
+ return match[1];
62
+ if (raw === "oh-my-openagent@latest")
63
+ return "latest";
64
+ }
65
+ return "unknown";
66
+ }
67
+ export const WakePlugin = async (input) => {
68
+ // Route subsequent log calls through the opencode runtime's structured
69
+ // log sink (TUI debug panel / journal / file) instead of raw stderr,
70
+ // which bleeds into the user's editor area.
71
+ logger.init({ client: input.client });
72
+ // Probe OMO version by reading the loaded config's plugin list.
73
+ let probed = "unknown";
74
+ try {
75
+ const c = input.client.config;
76
+ if (c && typeof c === "object" && "plugin" in c) {
77
+ const pluginField = c.plugin;
78
+ probed = probeOmoVersionFromPluginList(pluginField);
79
+ }
80
+ }
81
+ catch {
82
+ probed = "unknown";
83
+ }
84
+ const status = classifyOmoVersion(probed === "latest" ? undefined : probed);
85
+ if (status === "fully-fixed") {
86
+ logger.info("self-disabled (OMO fully-fixed)", { omoVersion: probed });
87
+ return {};
88
+ }
89
+ logger.info("wake-enforcer active", {
90
+ omoVersion: probed,
91
+ status,
92
+ debounceMs: WAKE_DEBOUNCE_MS,
93
+ backoffMs: WAKE_BACKOFF_MS.join("/"),
94
+ });
95
+ const dispatcher = new WakeDispatcher({
96
+ client: input.client,
97
+ directory: input.directory,
98
+ backoffMs: WAKE_BACKOFF_MS,
99
+ });
100
+ const coalescer = new WakeCoalescer({
101
+ debounceMs: WAKE_DEBOUNCE_MS,
102
+ onFire: (parent, children) => {
103
+ // Fire-and-forget; the dispatcher handles its own retries.
104
+ void dispatcher.dispatch(parent, children);
105
+ },
106
+ });
107
+ return {
108
+ event: async (evt) => {
109
+ const gate = shouldWakeOn(evt.event);
110
+ if (!gate.matched || !gate.sessionID)
111
+ return;
112
+ logger.debug("session.idle received", { sessionID: gate.sessionID });
113
+ const parentID = await lookupParentSessionID(input.client, gate.sessionID);
114
+ if (parentID === null) {
115
+ logger.debug("root session, no parent", { sessionID: gate.sessionID });
116
+ return;
117
+ }
118
+ coalescer.queue(parentID, gate.sessionID);
119
+ },
120
+ dispose: async () => {
121
+ coalescer.clear();
122
+ },
123
+ };
124
+ };
125
+ export default WakePlugin;
126
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/tui.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { TuiPluginModule } from "@opencode-ai/plugin/tui";
2
+ declare const id = "opencode-wake-plugin";
3
+ declare function tui(): Promise<void>;
4
+ declare const pluginModule: TuiPluginModule & {
5
+ id: string;
6
+ };
7
+ export default pluginModule;
8
+ export { id, tui };
9
+ //# sourceMappingURL=tui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,QAAA,MAAM,EAAE,yBAAyB,CAAC;AAElC,iBAAe,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAIlC;AAED,QAAA,MAAM,YAAY,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAGjD,CAAC;AAEF,eAAe,YAAY,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC"}
package/dist/tui.js ADDED
@@ -0,0 +1,32 @@
1
+ // opencode-wake-plugin
2
+ //
3
+ // TUI surface — intentionally minimal.
4
+ //
5
+ // Goal: make this plugin enumerate in the opencode TUI's external plugins
6
+ // panel alongside opencode-quota. Adding `tui` to `oc-plugin` and exporting
7
+ // the `./tui` entry point is what gates panel visibility.
8
+ //
9
+ // This plugin's actual value is server-side (session.idle → wake coalescer
10
+ // → dispatcher). It contributes zero TUI components and registers no
11
+ // commands/routes/dialogs/toasts. The `tui` function below is a no-op that
12
+ // completes successfully, satisfying the V1 plugin loader contract:
13
+ //
14
+ // export type TuiPluginModule = { id?: string; tui: TuiPlugin; server?: never };
15
+ // export type TuiPlugin = (api, options, meta) => Promise<void>;
16
+ //
17
+ // If we ever need a TUI surface (status banner, plugin info dialog, etc.),
18
+ // we add it here. Until then the no-op keeps the panel visibility win at
19
+ // minimal TUI/solid-js dependency cost.
20
+ const id = "opencode-wake-plugin";
21
+ async function tui() {
22
+ // No-op: server-side hooks in `src/server.ts` do all the work.
23
+ // A minimal log here would surface in the TUI's plugin-load panel.
24
+ // Intentionally silent to keep noise out of the TUI console.
25
+ }
26
+ const pluginModule = {
27
+ id,
28
+ tui,
29
+ };
30
+ export default pluginModule;
31
+ export { id, tui };
32
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,EAAE;AACF,uCAAuC;AACvC,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,0DAA0D;AAC1D,EAAE;AACF,2EAA2E;AAC3E,qEAAqE;AACrE,2EAA2E;AAC3E,oEAAoE;AACpE,EAAE;AACF,qFAAqF;AACrF,0EAA0E;AAC1E,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,wCAAwC;AAIxC,MAAM,EAAE,GAAG,sBAAsB,CAAC;AAElC,KAAK,UAAU,GAAG;IAChB,+DAA+D;IAC/D,mEAAmE;IACnE,6DAA6D;AAC/D,CAAC;AAED,MAAM,YAAY,GAAqC;IACrD,EAAE;IACF,GAAG;CACJ,CAAC;AAEF,eAAe,YAAY,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC"}
@@ -0,0 +1,15 @@
1
+ export type WakeCoalescerOptions = {
2
+ debounceMs: number;
3
+ onFire: (parentSessionID: string, childSessionIDs: string[]) => void;
4
+ };
5
+ export declare class WakeCoalescer {
6
+ private readonly timers;
7
+ private readonly pending;
8
+ private readonly debounceMs;
9
+ private readonly onFire;
10
+ constructor(opts: WakeCoalescerOptions);
11
+ queue(parentSessionID: string, childSessionID: string): void;
12
+ clear(): void;
13
+ private flush;
14
+ }
15
+ //# sourceMappingURL=wake-coalescer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wake-coalescer.d.ts","sourceRoot":"","sources":["../src/wake-coalescer.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACtE,CAAC;AAEF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAE5C,IAAI,EAAE,oBAAoB;IAKtC,KAAK,CAAC,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI;IAe5D,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,KAAK;CAQd"}
@@ -0,0 +1,43 @@
1
+ // opencode-wake-plugin
2
+ //
3
+ // WakeCoalescer buffers session.idle events for a small debounce window,
4
+ // then emits one coalesced wake per parent session. Each new child ID
5
+ // resets that parent's timer so a burst of wakes becomes a single dispatch.
6
+ export class WakeCoalescer {
7
+ timers = new Map();
8
+ pending = new Map();
9
+ debounceMs;
10
+ onFire;
11
+ constructor(opts) {
12
+ this.debounceMs = opts.debounceMs;
13
+ this.onFire = opts.onFire;
14
+ }
15
+ queue(parentSessionID, childSessionID) {
16
+ const list = this.pending.get(parentSessionID);
17
+ if (list) {
18
+ list.push(childSessionID);
19
+ }
20
+ else {
21
+ this.pending.set(parentSessionID, [childSessionID]);
22
+ }
23
+ const prev = this.timers.get(parentSessionID);
24
+ if (prev)
25
+ clearTimeout(prev);
26
+ this.timers.set(parentSessionID, setTimeout(() => this.flush(parentSessionID), this.debounceMs));
27
+ }
28
+ clear() {
29
+ for (const t of this.timers.values())
30
+ clearTimeout(t);
31
+ this.timers.clear();
32
+ this.pending.clear();
33
+ }
34
+ flush(parent) {
35
+ const children = this.pending.get(parent);
36
+ this.pending.delete(parent);
37
+ this.timers.delete(parent);
38
+ if (children && children.length > 0) {
39
+ this.onFire(parent, children);
40
+ }
41
+ }
42
+ }
43
+ //# sourceMappingURL=wake-coalescer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wake-coalescer.js","sourceRoot":"","sources":["../src/wake-coalescer.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,4EAA4E;AAS5E,MAAM,OAAO,aAAa;IACP,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IAClC,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IACtC,UAAU,CAAS;IACnB,MAAM,CAAiC;IAExD,YAAY,IAA0B;QACpC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,eAAuB,EAAE,cAAsB;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC/C,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC9C,IAAI,IAAI;YAAE,YAAY,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,eAAe,EACf,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAC/D,CAAC;IACJ,CAAC;IAED,KAAK;QACH,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,YAAY,CAAC,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,MAAc;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,68 @@
1
+ type PromptPart = {
2
+ type: "text";
3
+ text: string;
4
+ };
5
+ export type PathBodyPromptAsyncArgs = {
6
+ path: {
7
+ id: string;
8
+ };
9
+ body: {
10
+ parts: PromptPart[];
11
+ noReply: false;
12
+ model?: {
13
+ providerID: string;
14
+ modelID: string;
15
+ };
16
+ };
17
+ query: {
18
+ directory: string;
19
+ };
20
+ };
21
+ export type PromptAsyncArgs = PathBodyPromptAsyncArgs;
22
+ type PromptAsyncResult = {
23
+ error?: unknown;
24
+ } | undefined;
25
+ export type WakeDispatcherClient = {
26
+ session: {
27
+ messages(args: {
28
+ path: {
29
+ id: string;
30
+ };
31
+ query: {
32
+ directory: string;
33
+ };
34
+ }): Promise<unknown>;
35
+ promptAsync(args: PromptAsyncArgs): Promise<PromptAsyncResult>;
36
+ status?(args: {
37
+ query: {
38
+ directory: string;
39
+ };
40
+ }): Promise<unknown>;
41
+ };
42
+ };
43
+ export type WakeDispatcherOpts = {
44
+ client: WakeDispatcherClient;
45
+ directory: string;
46
+ /** Delays applied before retry attempts 1, 2, 3 (attempt 0 has zero delay). */
47
+ backoffMs: [number, number, number];
48
+ };
49
+ /**
50
+ * WakeDispatcher sends a chat.message-style wake to the parent session
51
+ * when subagent activity has been observed.
52
+ *
53
+ * Retries with exponential backoff (1s / 3s / 9s default) up to 3 retries
54
+ * (4 attempts total) before giving up. Survives `promptAsync` network
55
+ * failures without propagating the error to the plugin runtime.
56
+ */
57
+ export declare class WakeDispatcher {
58
+ private readonly client;
59
+ private readonly directory;
60
+ private readonly backoffMs;
61
+ constructor(opts: WakeDispatcherOpts);
62
+ private prompt;
63
+ private resolveParentModel;
64
+ private parentIsActive;
65
+ dispatch(parentSessionID: string, childSessionIDs: string[]): Promise<void>;
66
+ }
67
+ export {};
68
+ //# sourceMappingURL=wake-dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wake-dispatcher.d.ts","sourceRoot":"","sources":["../src/wake-dispatcher.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACrB,IAAI,EAAE;QACJ,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,CAAC;QACf,KAAK,CAAC,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KACjD,CAAC;IACF,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEtD,KAAK,iBAAiB,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC;AAMzD,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE;QACP,QAAQ,CAAC,IAAI,EAAE;YACb,IAAI,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAA;aAAE,CAAC;YACrB,KAAK,EAAE;gBAAE,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;SAC9B,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC/D,MAAM,CAAC,CAAC,IAAI,EAAE;YAAE,KAAK,EAAE;gBAAE,SAAS,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACnE,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;gBAEzC,IAAI,EAAE,kBAAkB;YAMtB,MAAM;YAeN,kBAAkB;YAgClB,cAAc;IAmBtB,QAAQ,CAAC,eAAe,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAwClF"}
@@ -0,0 +1,126 @@
1
+ import { buildWakeMessage } from "./contract.js";
2
+ import { logger } from "./logger.js";
3
+ function isRecord(value) {
4
+ return value !== null && typeof value === "object";
5
+ }
6
+ /**
7
+ * WakeDispatcher sends a chat.message-style wake to the parent session
8
+ * when subagent activity has been observed.
9
+ *
10
+ * Retries with exponential backoff (1s / 3s / 9s default) up to 3 retries
11
+ * (4 attempts total) before giving up. Survives `promptAsync` network
12
+ * failures without propagating the error to the plugin runtime.
13
+ */
14
+ export class WakeDispatcher {
15
+ client;
16
+ directory;
17
+ backoffMs;
18
+ constructor(opts) {
19
+ this.client = opts.client;
20
+ this.directory = opts.directory;
21
+ this.backoffMs = opts.backoffMs;
22
+ }
23
+ async prompt(parentSessionID, parts, model) {
24
+ const response = await this.client.session.promptAsync({
25
+ path: { id: parentSessionID },
26
+ body: { parts, noReply: false, ...(model ? { model } : {}) },
27
+ query: { directory: this.directory },
28
+ });
29
+ if (response?.error !== undefined && response.error !== null) {
30
+ throw response.error instanceof Error ? response.error : new Error(String(response.error));
31
+ }
32
+ }
33
+ async resolveParentModel(parentSessionID) {
34
+ try {
35
+ const response = await this.client.session.messages({
36
+ path: { id: parentSessionID },
37
+ query: { directory: this.directory },
38
+ });
39
+ const messages = isRecord(response) && Array.isArray(response["data"])
40
+ ? response["data"]
41
+ : Array.isArray(response)
42
+ ? response
43
+ : [];
44
+ for (let index = messages.length - 1; index >= 0; index--) {
45
+ const message = messages[index];
46
+ const info = isRecord(message) ? message["info"] : undefined;
47
+ if (!isRecord(info) || info["role"] !== "assistant")
48
+ continue;
49
+ const providerID = info["providerID"];
50
+ const modelID = info["modelID"];
51
+ if (typeof providerID === "string" && typeof modelID === "string") {
52
+ return { providerID, modelID };
53
+ }
54
+ }
55
+ }
56
+ catch (error) {
57
+ logger.debug("parent model lookup failed; preserving fallback wake", {
58
+ parentID: parentSessionID,
59
+ error: error instanceof Error ? error.message : String(error),
60
+ });
61
+ }
62
+ return undefined;
63
+ }
64
+ async parentIsActive(parentSessionID) {
65
+ if (!this.client.session.status)
66
+ return false;
67
+ try {
68
+ const response = await this.client.session.status({ query: { directory: this.directory } });
69
+ const statuses = isRecord(response) && isRecord(response["data"])
70
+ ? response["data"]
71
+ : response;
72
+ const status = isRecord(statuses) ? statuses[parentSessionID] : undefined;
73
+ const type = isRecord(status) ? status["type"] : undefined;
74
+ return type === "busy" || type === "retry" || type === "running";
75
+ }
76
+ catch (error) {
77
+ logger.debug("parent status lookup failed; preserving fallback wake", {
78
+ parentID: parentSessionID,
79
+ error: error instanceof Error ? error.message : String(error),
80
+ });
81
+ return false;
82
+ }
83
+ }
84
+ async dispatch(parentSessionID, childSessionIDs) {
85
+ if (await this.parentIsActive(parentSessionID)) {
86
+ logger.debug("skipping fallback wake; parent already active", { parentID: parentSessionID });
87
+ return;
88
+ }
89
+ logger.info("wake fired", {
90
+ parentID: parentSessionID,
91
+ childCount: childSessionIDs.length,
92
+ sessionIDs: childSessionIDs,
93
+ });
94
+ const msg = buildWakeMessage({
95
+ parentSessionID,
96
+ childSessionIDs,
97
+ timestamp: Date.now(),
98
+ });
99
+ const model = await this.resolveParentModel(parentSessionID);
100
+ const delays = [0, this.backoffMs[0], this.backoffMs[1], this.backoffMs[2]];
101
+ for (let attempt = 0; attempt < delays.length; attempt++) {
102
+ if (delays[attempt] > 0) {
103
+ await new Promise((r) => setTimeout(r, delays[attempt]));
104
+ }
105
+ logger.debug("dispatch attempt", {
106
+ parentID: parentSessionID,
107
+ attempt,
108
+ childCount: childSessionIDs.length,
109
+ willRetry: attempt < delays.length - 1,
110
+ });
111
+ try {
112
+ await this.prompt(parentSessionID, msg.parts, model);
113
+ return;
114
+ }
115
+ catch (e) {
116
+ if (attempt === delays.length - 1) {
117
+ logger.error("dispatch gave up after 4 attempts", {
118
+ parentID: parentSessionID,
119
+ error: e instanceof Error ? e.message : String(e),
120
+ });
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=wake-dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wake-dispatcher.js","sourceRoot":"","sources":["../src/wake-dispatcher.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "opencode-wake-plugin",
3
+ "version": "0.1.0",
4
+ "description": "Defense-in-depth plugin for the oh-my-openagent deferred-wake cascade. Wakes parent sessions on subagent idle and preserves the parent's last-used model.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./server": {
14
+ "types": "./dist/server.d.ts",
15
+ "default": "./dist/server.js"
16
+ },
17
+ "./tui": {
18
+ "types": "./dist/tui.d.ts",
19
+ "default": "./dist/tui.js"
20
+ }
21
+ },
22
+ "oc-plugin": [
23
+ "server",
24
+ "tui"
25
+ ],
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "README.md",
30
+ "LICENSE",
31
+ "CHANGELOG.md"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest",
38
+ "prepublishOnly": "bun run typecheck && bun run test && bun run build"
39
+ },
40
+ "keywords": [
41
+ "opencode",
42
+ "opencode-plugin",
43
+ "oh-my-openagent",
44
+ "wake",
45
+ "subagent",
46
+ "cascade",
47
+ "omo"
48
+ ],
49
+ "author": {
50
+ "name": "opencode-wake-plugin contributors"
51
+ },
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "https://github.com/desendoo/opencode-wake-plugin.git"
55
+ },
56
+ "bugs": {
57
+ "url": "https://github.com/desendoo/opencode-wake-plugin/issues"
58
+ },
59
+ "homepage": "https://github.com/desendoo/opencode-wake-plugin#readme",
60
+ "license": "MIT",
61
+ "peerDependencies": {
62
+ "@opencode-ai/plugin": "^1.16.0"
63
+ },
64
+ "devDependencies": {
65
+ "@opencode-ai/plugin": "1.16.2",
66
+ "@types/node": "22.10.0",
67
+ "typescript": "5.6.3",
68
+ "vitest": "3.2.7"
69
+ },
70
+ "engines": {
71
+ "node": ">=22"
72
+ }
73
+ }