jeopi-agent-core 16.2.13

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 (66) hide show
  1. package/CHANGELOG.md +1016 -0
  2. package/README.md +473 -0
  3. package/dist/types/agent-loop.d.ts +66 -0
  4. package/dist/types/agent.d.ts +427 -0
  5. package/dist/types/append-only-context.d.ts +133 -0
  6. package/dist/types/compaction/branch-summarization.d.ts +101 -0
  7. package/dist/types/compaction/compaction-v2-streaming.d.ts +82 -0
  8. package/dist/types/compaction/compaction.d.ts +283 -0
  9. package/dist/types/compaction/entries.d.ts +110 -0
  10. package/dist/types/compaction/errors.d.ts +26 -0
  11. package/dist/types/compaction/index.d.ts +12 -0
  12. package/dist/types/compaction/messages.d.ts +77 -0
  13. package/dist/types/compaction/openai.d.ts +77 -0
  14. package/dist/types/compaction/pruning.d.ts +105 -0
  15. package/dist/types/compaction/shake.d.ts +92 -0
  16. package/dist/types/compaction/tool-protection.d.ts +17 -0
  17. package/dist/types/compaction/utils.d.ts +58 -0
  18. package/dist/types/compaction.d.ts +1 -0
  19. package/dist/types/index.d.ts +12 -0
  20. package/dist/types/proxy.d.ts +85 -0
  21. package/dist/types/replay-policy.d.ts +5 -0
  22. package/dist/types/run-collector.d.ts +196 -0
  23. package/dist/types/telemetry.d.ts +590 -0
  24. package/dist/types/thinking.d.ts +17 -0
  25. package/dist/types/tokenizer.d.ts +1 -0
  26. package/dist/types/types.d.ts +640 -0
  27. package/dist/types/utils/yield.d.ts +71 -0
  28. package/package.json +78 -0
  29. package/src/agent-loop.ts +2188 -0
  30. package/src/agent.ts +1457 -0
  31. package/src/append-only-context.ts +348 -0
  32. package/src/compaction/branch-summarization.ts +370 -0
  33. package/src/compaction/compaction-v2-streaming.ts +719 -0
  34. package/src/compaction/compaction.ts +1553 -0
  35. package/src/compaction/entries.ts +142 -0
  36. package/src/compaction/errors.ts +31 -0
  37. package/src/compaction/index.ts +13 -0
  38. package/src/compaction/messages.ts +237 -0
  39. package/src/compaction/openai.ts +581 -0
  40. package/src/compaction/prompts/auto-handoff-threshold-focus.md +1 -0
  41. package/src/compaction/prompts/branch-summary-context.md +5 -0
  42. package/src/compaction/prompts/branch-summary-preamble.md +2 -0
  43. package/src/compaction/prompts/branch-summary.md +30 -0
  44. package/src/compaction/prompts/compaction-short-summary.md +9 -0
  45. package/src/compaction/prompts/compaction-summary-context.md +5 -0
  46. package/src/compaction/prompts/compaction-summary.md +38 -0
  47. package/src/compaction/prompts/compaction-turn-prefix.md +17 -0
  48. package/src/compaction/prompts/compaction-update-summary.md +45 -0
  49. package/src/compaction/prompts/file-operations.md +5 -0
  50. package/src/compaction/prompts/handoff-document.md +49 -0
  51. package/src/compaction/prompts/snapcompact-archive-context.md +3 -0
  52. package/src/compaction/prompts/summarization-system.md +3 -0
  53. package/src/compaction/pruning.ts +424 -0
  54. package/src/compaction/shake.ts +429 -0
  55. package/src/compaction/tool-protection.ts +55 -0
  56. package/src/compaction/utils.ts +323 -0
  57. package/src/compaction.ts +1 -0
  58. package/src/index.ts +24 -0
  59. package/src/proxy.ts +376 -0
  60. package/src/replay-policy.ts +13 -0
  61. package/src/run-collector.ts +631 -0
  62. package/src/telemetry.ts +2034 -0
  63. package/src/thinking.ts +19 -0
  64. package/src/tokenizer.ts +17 -0
  65. package/src/types.ts +718 -0
  66. package/src/utils/yield.ts +183 -0
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Cooperative yield utility for preventing Bun event-loop busy-wait.
3
+ *
4
+ * ## Root Cause
5
+ *
6
+ * Bun 1.3.x (JavaScriptCore) event loop busy-waits (spins in userspace)
7
+ * when the only pending work is an unresolved Promise — even if there are
8
+ * active I/O watchers (stdin, child process pipes, etc.). The event loop
9
+ * continuously polls for microtask resolution instead of blocking in
10
+ * `epoll_wait`, consuming ~100% of a CPU core.
11
+ *
12
+ * This affects any `await` on a never-resolved Promise, including:
13
+ * - `Promise.withResolvers()` used for user input callbacks
14
+ * - `await proc.exited` for long-running child processes
15
+ * - Agent loop iterations waiting for the next tool call
16
+ *
17
+ * ## Fix
18
+ *
19
+ * A recurring `setInterval` keeps the event loop sleeping in `epoll_wait`.
20
+ * The `EventLoopKeepalive` class and `keepaliveWhile()` wrapper provide a
21
+ * clean way to install and clean up this keepalive timer.
22
+ *
23
+ * The older `yieldIfDue()` and `ExponentialYield` approaches (compensated
24
+ * sleep loops) are retained for the agent-loop hot-path where Promises
25
+ * resolve frequently and the keepalive alone is insufficient.
26
+ */
27
+
28
+ import { scheduler } from "node:timers/promises";
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // EventLoopKeepalive — the primary fix for idle-state busy-wait
32
+ // ---------------------------------------------------------------------------
33
+
34
+ export class EventLoopKeepalive {
35
+ #tmr = setInterval(() => {}, 86_400_000).unref();
36
+ [Symbol.dispose](): void {
37
+ clearInterval(this.#tmr);
38
+ }
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // yieldIfDue — retained for agent-loop hot-path
43
+ // ---------------------------------------------------------------------------
44
+
45
+ const YIELD_SLEEP_MS = 20;
46
+ const YIELD_INTERVAL_MS = 50;
47
+
48
+ /**
49
+ * Sleep for at least `ms` milliseconds of wall-clock time.
50
+ * Retries the wait if it returns prematurely (which can happen when napi
51
+ * callbacks wake the event loop via `uv_async_send`). When `signal` is
52
+ * provided, the wait is cancellable and silently returns on abort instead
53
+ * of throwing — callers race against another promise that decides what to
54
+ * do next.
55
+ */
56
+ async function sleepAtLeast(ms: number, signal?: AbortSignal): Promise<void> {
57
+ const start = performance.now();
58
+ let remaining = ms;
59
+ while (remaining > 0) {
60
+ if (signal?.aborted) return;
61
+ try {
62
+ await scheduler.wait(remaining, { signal });
63
+ } catch (err) {
64
+ if ((err as { name?: string })?.name === "AbortError") return;
65
+ throw err;
66
+ }
67
+ remaining = ms - (performance.now() - start);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Cooperative yield gate. Sleeps for at least {@link YieldGateOptions.sleepMs}
73
+ * but at most once every {@link YieldGateOptions.intervalMs}; hot-path callers
74
+ * invoke it freely and only the slow path actually sleeps.
75
+ *
76
+ * The clock and sleep are injectable so tests drive the gate logic without
77
+ * touching process-global `Date.now`/`scheduler.wait` — globals a concurrent
78
+ * test file can restore mid-run, which previously made the shared gate flake.
79
+ */
80
+ export interface YieldGateOptions {
81
+ now?: () => number;
82
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
83
+ intervalMs?: number;
84
+ sleepMs?: number;
85
+ }
86
+
87
+ export class YieldGate {
88
+ #lastYieldAt = 0;
89
+ readonly #now: () => number;
90
+ readonly #sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
91
+ readonly #intervalMs: number;
92
+ readonly #sleepMs: number;
93
+
94
+ constructor(opts: YieldGateOptions = {}) {
95
+ this.#now = opts.now ?? (() => Date.now());
96
+ this.#sleep = opts.sleep ?? sleepAtLeast;
97
+ this.#intervalMs = opts.intervalMs ?? YIELD_INTERVAL_MS;
98
+ this.#sleepMs = opts.sleepMs ?? YIELD_SLEEP_MS;
99
+ }
100
+
101
+ async yieldIfDue(signal?: AbortSignal): Promise<void> {
102
+ const now = this.#now();
103
+ const elapsed = now - this.#lastYieldAt;
104
+ // `elapsed < 0` means the wall clock moved backward relative to the last
105
+ // yield (NTP step, fake-timer test, or a stale future timestamp left by
106
+ // another caller): treat it as due and re-anchor rather than gate forever.
107
+ if (elapsed >= 0 && elapsed < this.#intervalMs) return;
108
+ await this.#sleep(this.#sleepMs, signal);
109
+ this.#lastYieldAt = this.#now();
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Process-wide gate shared by all hot-path callers so tight loops collectively
115
+ * respect the interval rather than each sleeping independently.
116
+ */
117
+ const sharedYieldGate = new YieldGate();
118
+
119
+ /**
120
+ * Yield to the Bun event loop, sleeping for at least 20 ms — but at most once
121
+ * every {@link YIELD_INTERVAL_MS} across all callers.
122
+ */
123
+ export function yieldIfDue(): Promise<void> {
124
+ return sharedYieldGate.yieldIfDue();
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // ExponentialYield — retained for bash-executor long waits
129
+ // ---------------------------------------------------------------------------
130
+
131
+ const EXP_DEFAULT_MIN_MS = 20;
132
+ const EXP_DEFAULT_MAX_MS = 10_000;
133
+ const EXP_DEFAULT_MULTIPLIER = 2;
134
+
135
+ export class ExponentialYield {
136
+ #currentMs: number;
137
+ readonly #minMs: number;
138
+ readonly #maxMs: number;
139
+ readonly #multiplier: number;
140
+
141
+ constructor(opts?: { minMs?: number; maxMs?: number; multiplier?: number }) {
142
+ this.#minMs = opts?.minMs ?? EXP_DEFAULT_MIN_MS;
143
+ this.#maxMs = opts?.maxMs ?? EXP_DEFAULT_MAX_MS;
144
+ this.#multiplier = opts?.multiplier ?? EXP_DEFAULT_MULTIPLIER;
145
+ this.#currentMs = this.#minMs;
146
+ }
147
+
148
+ notifyActivity(): void {
149
+ this.#currentMs = this.#minMs;
150
+ }
151
+
152
+ async sleep(signal?: AbortSignal): Promise<number> {
153
+ const ms = this.#currentMs;
154
+ await sleepAtLeast(ms, signal);
155
+ this.#currentMs = Math.min(this.#currentMs * this.#multiplier, this.#maxMs);
156
+ return ms;
157
+ }
158
+
159
+ /**
160
+ * Race `racers` against an exponentially-backed-off cooperative yield.
161
+ * The losing sleep is cancelled as soon as a racer settles, so no stray
162
+ * timers keep the event loop alive past the racer's resolution.
163
+ */
164
+ async race<T>(racers: Array<Promise<T>>): Promise<T> {
165
+ const racer = Promise.race(racers);
166
+ const controller = new AbortController();
167
+ try {
168
+ const yieldMarker = Symbol("exp-yield");
169
+ for (;;) {
170
+ const result = await Promise.race<T | typeof yieldMarker>([
171
+ racer,
172
+ this.sleep(controller.signal).then(() => yieldMarker as T | typeof yieldMarker),
173
+ ]);
174
+ if (result !== yieldMarker) {
175
+ this.notifyActivity();
176
+ return result;
177
+ }
178
+ }
179
+ } finally {
180
+ controller.abort();
181
+ }
182
+ }
183
+ }