@corenel/harness 0.1.0 → 0.3.2

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 (180) hide show
  1. package/dist/acp/agentMap.d.ts +34 -0
  2. package/dist/acp/agentMap.d.ts.map +1 -0
  3. package/dist/acp/agentMap.js +110 -0
  4. package/dist/acp/agentMap.js.map +1 -0
  5. package/dist/acp/index.d.ts +4 -0
  6. package/dist/acp/index.d.ts.map +1 -0
  7. package/dist/acp/index.js +6 -0
  8. package/dist/acp/index.js.map +1 -0
  9. package/dist/acp/policyMap.d.ts +55 -0
  10. package/dist/acp/policyMap.d.ts.map +1 -0
  11. package/dist/acp/policyMap.js +121 -0
  12. package/dist/acp/policyMap.js.map +1 -0
  13. package/dist/acp/types.d.ts +41 -0
  14. package/dist/acp/types.d.ts.map +1 -0
  15. package/dist/acp/types.js +14 -0
  16. package/dist/acp/types.js.map +1 -0
  17. package/dist/api/backend.d.ts +39 -0
  18. package/dist/api/backend.d.ts.map +1 -0
  19. package/dist/api/backend.js +126 -0
  20. package/dist/api/backend.js.map +1 -0
  21. package/dist/api/client.d.ts +95 -0
  22. package/dist/api/client.d.ts.map +1 -0
  23. package/dist/api/client.js +212 -0
  24. package/dist/api/client.js.map +1 -0
  25. package/dist/api/fileMirrorSource.d.ts +24 -0
  26. package/dist/api/fileMirrorSource.d.ts.map +1 -0
  27. package/dist/api/fileMirrorSource.js +59 -0
  28. package/dist/api/fileMirrorSource.js.map +1 -0
  29. package/dist/api/mirror.d.ts +102 -0
  30. package/dist/api/mirror.d.ts.map +1 -0
  31. package/dist/api/mirror.js +204 -0
  32. package/dist/api/mirror.js.map +1 -0
  33. package/dist/api/mirrorScheduler.d.ts +56 -0
  34. package/dist/api/mirrorScheduler.d.ts.map +1 -0
  35. package/dist/api/mirrorScheduler.js +167 -0
  36. package/dist/api/mirrorScheduler.js.map +1 -0
  37. package/dist/conversation.d.ts +0 -11
  38. package/dist/conversation.d.ts.map +1 -1
  39. package/dist/conversation.js +1 -24
  40. package/dist/conversation.js.map +1 -1
  41. package/dist/core/budget.d.ts +40 -0
  42. package/dist/core/budget.d.ts.map +1 -0
  43. package/dist/core/budget.js +62 -0
  44. package/dist/core/budget.js.map +1 -0
  45. package/dist/core/loop.d.ts +11 -0
  46. package/dist/core/loop.d.ts.map +1 -1
  47. package/dist/core/loop.js +89 -20
  48. package/dist/core/loop.js.map +1 -1
  49. package/dist/core/pricing.d.ts +8 -0
  50. package/dist/core/pricing.d.ts.map +1 -0
  51. package/dist/core/pricing.js +12 -0
  52. package/dist/core/pricing.js.map +1 -0
  53. package/dist/core/recovery.d.ts +60 -0
  54. package/dist/core/recovery.d.ts.map +1 -0
  55. package/dist/core/recovery.js +128 -0
  56. package/dist/core/recovery.js.map +1 -0
  57. package/dist/guardrail/builtins.js +6 -6
  58. package/dist/guardrail/builtins.js.map +1 -1
  59. package/dist/guardrail/permission-service.js +1 -1
  60. package/dist/guardrail/permission-service.js.map +1 -1
  61. package/dist/guardrail/serialize.d.ts.map +1 -1
  62. package/dist/guardrail/serialize.js +6 -0
  63. package/dist/guardrail/serialize.js.map +1 -1
  64. package/dist/guardrail/validate.d.ts.map +1 -1
  65. package/dist/guardrail/validate.js +13 -0
  66. package/dist/guardrail/validate.js.map +1 -1
  67. package/dist/install/lockfile.d.ts +79 -0
  68. package/dist/install/lockfile.d.ts.map +1 -0
  69. package/dist/install/lockfile.js +155 -0
  70. package/dist/install/lockfile.js.map +1 -0
  71. package/dist/install/store.d.ts +13 -0
  72. package/dist/install/store.d.ts.map +1 -0
  73. package/dist/install/store.js +38 -0
  74. package/dist/install/store.js.map +1 -0
  75. package/dist/log.d.ts.map +1 -1
  76. package/dist/log.js +6 -1
  77. package/dist/log.js.map +1 -1
  78. package/dist/prompts/host.d.ts +4 -0
  79. package/dist/prompts/host.d.ts.map +1 -1
  80. package/dist/prompts/host.js.map +1 -1
  81. package/dist/prompts/index.d.ts +6 -0
  82. package/dist/prompts/index.d.ts.map +1 -1
  83. package/dist/prompts/index.js +16 -6
  84. package/dist/prompts/index.js.map +1 -1
  85. package/dist/prompts/library/defaults.generated.d.ts.map +1 -1
  86. package/dist/prompts/library/defaults.generated.js +22 -4
  87. package/dist/prompts/library/defaults.generated.js.map +1 -1
  88. package/dist/prompts/toolsMarker.d.ts +13 -0
  89. package/dist/prompts/toolsMarker.d.ts.map +1 -0
  90. package/dist/prompts/toolsMarker.js +21 -0
  91. package/dist/prompts/toolsMarker.js.map +1 -0
  92. package/dist/providers/gateway.d.ts +19 -1
  93. package/dist/providers/gateway.d.ts.map +1 -1
  94. package/dist/providers/gateway.js +33 -4
  95. package/dist/providers/gateway.js.map +1 -1
  96. package/dist/services.d.ts +13 -0
  97. package/dist/services.d.ts.map +1 -1
  98. package/dist/services.js +2 -0
  99. package/dist/services.js.map +1 -1
  100. package/dist/sessions/apiLeaseStore.d.ts +10 -0
  101. package/dist/sessions/apiLeaseStore.d.ts.map +1 -0
  102. package/dist/sessions/apiLeaseStore.js +41 -0
  103. package/dist/sessions/apiLeaseStore.js.map +1 -0
  104. package/dist/sessions/fileRecallStore.d.ts +33 -0
  105. package/dist/sessions/fileRecallStore.d.ts.map +1 -0
  106. package/dist/sessions/fileRecallStore.js +112 -0
  107. package/dist/sessions/fileRecallStore.js.map +1 -0
  108. package/dist/sessions/fileSessionStore.d.ts +20 -0
  109. package/dist/sessions/fileSessionStore.d.ts.map +1 -0
  110. package/dist/sessions/fileSessionStore.js +151 -0
  111. package/dist/sessions/fileSessionStore.js.map +1 -0
  112. package/dist/sessions/head.d.ts +90 -0
  113. package/dist/sessions/head.d.ts.map +1 -0
  114. package/dist/sessions/head.js +82 -0
  115. package/dist/sessions/head.js.map +1 -0
  116. package/dist/sessions/inflight.d.ts +58 -0
  117. package/dist/sessions/inflight.d.ts.map +1 -0
  118. package/dist/sessions/inflight.js +0 -0
  119. package/dist/sessions/inflight.js.map +1 -0
  120. package/dist/sessions/jsonl.d.ts +23 -0
  121. package/dist/sessions/jsonl.d.ts.map +1 -0
  122. package/dist/sessions/jsonl.js +52 -0
  123. package/dist/sessions/jsonl.js.map +1 -0
  124. package/dist/sessions/lease.d.ts +59 -0
  125. package/dist/sessions/lease.d.ts.map +1 -0
  126. package/dist/sessions/lease.js +115 -0
  127. package/dist/sessions/lease.js.map +1 -0
  128. package/dist/sessions/leaseStore.d.ts +46 -0
  129. package/dist/sessions/leaseStore.d.ts.map +1 -0
  130. package/dist/sessions/leaseStore.js +58 -0
  131. package/dist/sessions/leaseStore.js.map +1 -0
  132. package/dist/sessions/testFs.d.ts +5 -0
  133. package/dist/sessions/testFs.d.ts.map +1 -0
  134. package/dist/sessions/testFs.js +85 -0
  135. package/dist/sessions/testFs.js.map +1 -0
  136. package/dist/sessions/timeline.d.ts +134 -0
  137. package/dist/sessions/timeline.d.ts.map +1 -0
  138. package/dist/sessions/timeline.js +127 -0
  139. package/dist/sessions/timeline.js.map +1 -0
  140. package/dist/sidecar/host.d.ts +284 -2
  141. package/dist/sidecar/host.d.ts.map +1 -1
  142. package/dist/sidecar/host.js +725 -1
  143. package/dist/sidecar/host.js.map +1 -1
  144. package/dist/sidecar/server.d.ts +217 -2
  145. package/dist/sidecar/server.d.ts.map +1 -1
  146. package/dist/sidecar/server.js +828 -3
  147. package/dist/sidecar/server.js.map +1 -1
  148. package/dist/skills.d.ts +8 -3
  149. package/dist/skills.d.ts.map +1 -1
  150. package/dist/skills.js +71 -35
  151. package/dist/skills.js.map +1 -1
  152. package/dist/snapshot/effects.d.ts +69 -0
  153. package/dist/snapshot/effects.d.ts.map +1 -0
  154. package/dist/snapshot/effects.js +67 -0
  155. package/dist/snapshot/effects.js.map +1 -0
  156. package/dist/snapshot/manifest.d.ts +73 -0
  157. package/dist/snapshot/manifest.d.ts.map +1 -0
  158. package/dist/snapshot/manifest.js +141 -0
  159. package/dist/snapshot/manifest.js.map +1 -0
  160. package/dist/snapshot/tree.d.ts +62 -0
  161. package/dist/snapshot/tree.d.ts.map +1 -0
  162. package/dist/snapshot/tree.js +147 -0
  163. package/dist/snapshot/tree.js.map +1 -0
  164. package/dist/tools/builtins.d.ts +1 -0
  165. package/dist/tools/builtins.d.ts.map +1 -1
  166. package/dist/tools/builtins.js +150 -5
  167. package/dist/tools/builtins.js.map +1 -1
  168. package/dist/tools/files.d.ts.map +1 -1
  169. package/dist/tools/files.js +13 -0
  170. package/dist/tools/files.js.map +1 -1
  171. package/dist/tools/http.d.ts.map +1 -1
  172. package/dist/tools/http.js.map +1 -1
  173. package/dist/tools/namespaces.d.ts +1 -1
  174. package/dist/tools/namespaces.d.ts.map +1 -1
  175. package/dist/tools/namespaces.js +24 -0
  176. package/dist/tools/namespaces.js.map +1 -1
  177. package/dist/tools/todo.d.ts.map +1 -1
  178. package/dist/tools/todo.js +12 -0
  179. package/dist/tools/todo.js.map +1 -1
  180. package/package.json +26 -34
@@ -0,0 +1,204 @@
1
+ import { LeaseConflict } from './client';
2
+ const blank = () => ({
3
+ pushedIndex: 0, lastPushedAt: 0, lastError: '', authoritative: false, epoch: 0,
4
+ });
5
+ export class SessionMirror {
6
+ constructor(deps) {
7
+ Object.defineProperty(this, "deps", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: deps
12
+ });
13
+ Object.defineProperty(this, "state", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: new Map()
18
+ });
19
+ /** local session id -> the id this backend knows it by. Two namespaces, and
20
+ * assuming they are one is a silent 404 on every push. */
21
+ Object.defineProperty(this, "remoteIds", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: new Map()
26
+ });
27
+ Object.defineProperty(this, "now", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: void 0
32
+ });
33
+ this.now = deps.now ?? Date.now;
34
+ }
35
+ /**
36
+ * The backend's id for a local session, creating the record if this is the
37
+ * first time it has been mentioned.
38
+ *
39
+ * Idempotent on the server, so a reconnect finds the session it was already
40
+ * pushing to rather than opening a second one beside it and splitting the log.
41
+ */
42
+ async remoteId(sessionId) {
43
+ const known = this.remoteIds.get(sessionId);
44
+ if (known)
45
+ return known;
46
+ const found = await this.deps.client.findByLocalId(sessionId);
47
+ if (found) {
48
+ this.remoteIds.set(sessionId, found._id);
49
+ return found._id;
50
+ }
51
+ try {
52
+ const created = await this.deps.client.createSession({ localId: sessionId, location: 'local' });
53
+ this.remoteIds.set(sessionId, created._id);
54
+ return created._id;
55
+ }
56
+ catch (err) {
57
+ // A SANDBOX credential cannot create sessions, on purpose: the user
58
+ // provisions the session and the credential together, and the machine
59
+ // finds them. Saying that plainly beats reporting the raw 401, which reads
60
+ // like a broken credential rather than a missing session.
61
+ const status = err.status;
62
+ if (status === 401 || status === 403) {
63
+ throw new Error(`no session on the backend for "${sessionId}", and this credential may not create one — `
64
+ + 'provision the session first, or use a user credential');
65
+ }
66
+ throw err;
67
+ }
68
+ }
69
+ status(sessionId) {
70
+ return { ...(this.state.get(sessionId) ?? blank()) };
71
+ }
72
+ set(sessionId, patch) {
73
+ const next = { ...(this.state.get(sessionId) ?? blank()), ...patch };
74
+ this.state.set(sessionId, next);
75
+ return { ...next };
76
+ }
77
+ /**
78
+ * Claim authority for this machine.
79
+ *
80
+ * Reads the current epoch and takes the next one. Losing the race is a normal
81
+ * answer, not an error: another machine holds the session, and what this one
82
+ * does about that is a decision for the caller (usually: stop, and offer the
83
+ * user a demote).
84
+ */
85
+ async claim(sessionId) {
86
+ try {
87
+ const remote = await this.remoteId(sessionId);
88
+ const current = await this.deps.client.getSession(remote);
89
+ const lease = await this.deps.client.acquireLease(remote, current.epoch, this.deps.holder);
90
+ this.deps.log?.(`mirror: took authority for ${sessionId} at epoch ${lease.epoch}`);
91
+ return { ok: true, status: this.set(sessionId, { authoritative: true, epoch: lease.epoch, lastError: '' }) };
92
+ }
93
+ catch (err) {
94
+ const lost = err instanceof LeaseConflict;
95
+ const message = err instanceof Error ? err.message : String(err);
96
+ this.deps.log?.(`mirror: could not take authority for ${sessionId}: ${message}`);
97
+ return {
98
+ ok: false,
99
+ status: this.set(sessionId, {
100
+ authoritative: false,
101
+ ...(lost ? { epoch: err.currentEpoch } : {}),
102
+ lastError: message,
103
+ }),
104
+ };
105
+ }
106
+ }
107
+ /** Give authority up, so another machine can take it without waiting for an
108
+ * expiry that does not exist. Best-effort like everything else here. */
109
+ async release(sessionId) {
110
+ const s = this.status(sessionId);
111
+ if (!s.authoritative)
112
+ return;
113
+ try {
114
+ const epoch = await this.deps.client.releaseLease(await this.remoteId(sessionId), s.epoch);
115
+ this.set(sessionId, { authoritative: false, epoch });
116
+ }
117
+ catch (err) {
118
+ // A failed release is not worth surfacing: the next holder's acquire will
119
+ // supersede this epoch anyway. That is the whole point of a monotonic
120
+ // counter over a flag.
121
+ this.set(sessionId, { authoritative: false, lastError: err instanceof Error ? err.message : String(err) });
122
+ }
123
+ }
124
+ /**
125
+ * Push everything the mirror has not seen.
126
+ *
127
+ * Resumes from the recorded cursor, so an interrupted push costs a retry rather
128
+ * than a re-upload of the whole log. Resolves either way — a caller in the
129
+ * middle of an agent turn must never have to handle this throwing.
130
+ */
131
+ async push(sessionId) {
132
+ const before = this.status(sessionId);
133
+ if (!before.authoritative) {
134
+ // Pushing without authority would overwrite whichever machine is actually
135
+ // running the session. Refusing is the correct behaviour, not a limitation.
136
+ return { ok: false, pushed: 0, status: this.set(sessionId, { lastError: 'not the authoritative holder' }) };
137
+ }
138
+ try {
139
+ const remote = await this.remoteId(sessionId);
140
+ const { entries, nextIndex } = await this.deps.local.entriesSince(sessionId, before.pushedIndex);
141
+ if (entries.length) {
142
+ await this.deps.client.appendTimeline(remote, before.epoch, entries);
143
+ }
144
+ const head = await this.deps.local.head(sessionId);
145
+ if (head)
146
+ await this.deps.client.putHead(remote, before.epoch, head);
147
+ return {
148
+ ok: true,
149
+ pushed: entries.length,
150
+ status: this.set(sessionId, {
151
+ pushedIndex: Math.max(before.pushedIndex, nextIndex),
152
+ lastPushedAt: this.now(),
153
+ lastError: '',
154
+ }),
155
+ };
156
+ }
157
+ catch (err) {
158
+ const message = err instanceof Error ? err.message : String(err);
159
+ const lostAuthority = err instanceof LeaseConflict;
160
+ if (lostAuthority) {
161
+ // Someone else took the session. This machine's copy is a stale branch
162
+ // now, and continuing to push would be fighting over a session it no
163
+ // longer holds. Stop, and let the caller decide what to tell the user.
164
+ this.deps.log?.(`mirror: lost authority for ${sessionId}; stopping`);
165
+ return {
166
+ ok: false,
167
+ pushed: 0,
168
+ lostAuthority: true,
169
+ status: this.set(sessionId, {
170
+ authoritative: false,
171
+ epoch: err.currentEpoch,
172
+ lastError: 'another machine took this session',
173
+ }),
174
+ };
175
+ }
176
+ this.deps.log?.(`mirror: push failed for ${sessionId}: ${message}`);
177
+ return { ok: false, pushed: 0, status: this.set(sessionId, { lastError: message }) };
178
+ }
179
+ }
180
+ /**
181
+ * Read the mirror.
182
+ *
183
+ * RETURNS the remote state; it does not write anything locally. What to do with
184
+ * a remote copy — restore it, merge it, show it read-only — belongs to whoever
185
+ * owns the local store, and a mirror that silently overwrote local state would
186
+ * have made itself the original.
187
+ */
188
+ async pull(sessionId, since = 0) {
189
+ try {
190
+ const { entries, nextIndex } = await this.deps.client.readTimeline(await this.remoteId(sessionId), since);
191
+ return { ok: true, entries, nextIndex, error: '' };
192
+ }
193
+ catch (err) {
194
+ return { ok: false, entries: [], nextIndex: 0, error: err instanceof Error ? err.message : String(err) };
195
+ }
196
+ }
197
+ /** Forget local bookkeeping for a session — after a destroy, or when this
198
+ * machine stops caring about it. Does not touch the remote copy. */
199
+ forget(sessionId) {
200
+ this.state.delete(sessionId);
201
+ this.remoteIds.delete(sessionId);
202
+ }
203
+ }
204
+ //# sourceMappingURL=mirror.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror.js","sourceRoot":"","sources":["../../api/mirror.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { PushResult } from './mirror';
2
+ /** Just enough of SessionMirror to schedule against — so a test needs no client. */
3
+ export interface SchedulableMirror {
4
+ push(sessionId: string): Promise<PushResult>;
5
+ }
6
+ export interface MirrorSchedulerDeps {
7
+ mirror: SchedulableMirror;
8
+ /** Quiet period after the last write before pushing. */
9
+ debounceMs?: number;
10
+ /** How far behind the mirror may fall while writes keep coming. */
11
+ maxWaitMs?: number;
12
+ /** Wait after a failed push before trying again. Doubles, to a cap. */
13
+ retryMs?: number;
14
+ maxRetryMs?: number;
15
+ setTimer?: (fn: () => void, ms: number) => unknown;
16
+ clearTimer?: (handle: unknown) => void;
17
+ now?: () => number;
18
+ log?: (line: string) => void;
19
+ }
20
+ export declare class MirrorScheduler {
21
+ private readonly deps;
22
+ private readonly pending;
23
+ private readonly cfg;
24
+ private readonly setTimer;
25
+ private readonly clearTimer;
26
+ private readonly now;
27
+ constructor(deps: MirrorSchedulerDeps);
28
+ private slot;
29
+ private disarm;
30
+ /**
31
+ * Work happened; push soon.
32
+ *
33
+ * Cheap and synchronous — this is called from the write path, and the write
34
+ * path must never wait on the mirror.
35
+ */
36
+ touch(sessionId: string): void;
37
+ /**
38
+ * Push now and wait for the result.
39
+ *
40
+ * For pause, handoff and shutdown — the moments where "shortly" is not good
41
+ * enough because the machine may be about to stop being the one that has the
42
+ * session.
43
+ */
44
+ flush(sessionId: string): Promise<PushResult>;
45
+ /** Flush every session with work outstanding — shutdown, or a demote of the
46
+ * whole machine. Runs them together; one failure does not stop the others. */
47
+ flushAll(): Promise<Record<string, PushResult>>;
48
+ private run;
49
+ /** Whether a push is scheduled or in flight. */
50
+ pending_(sessionId: string): boolean;
51
+ /** Stop scheduling for a session and forget it — a destroy, or a handoff that
52
+ * has already flushed. */
53
+ stop(sessionId: string): void;
54
+ dispose(): void;
55
+ }
56
+ //# sourceMappingURL=mirrorScheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirrorScheduler.d.ts","sourceRoot":"","sources":["../../api/mirrorScheduler.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,oFAAoF;AACpF,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAgBD,qBAAa,eAAe;IAOd,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4B;IACvD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEN,IAAI,EAAE,mBAAmB;IAYtD,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,MAAM;IAOd;;;;;OAKG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAqB9B;;;;;;OAMG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAMnD;kFAC8E;IACxE,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAMvC,GAAG;IA2CjB,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAKpC;8BAC0B;IAC1B,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAO7B,OAAO,IAAI,IAAI;CAIhB"}
@@ -0,0 +1,167 @@
1
+ const DEFAULTS = { debounceMs: 2000, maxWaitMs: 15000, retryMs: 5000, maxRetryMs: 60000 };
2
+ export class MirrorScheduler {
3
+ constructor(deps) {
4
+ Object.defineProperty(this, "deps", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: deps
9
+ });
10
+ Object.defineProperty(this, "pending", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: new Map()
15
+ });
16
+ Object.defineProperty(this, "cfg", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: void 0
21
+ });
22
+ Object.defineProperty(this, "setTimer", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
28
+ Object.defineProperty(this, "clearTimer", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ Object.defineProperty(this, "now", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: void 0
39
+ });
40
+ this.cfg = {
41
+ debounceMs: deps.debounceMs ?? DEFAULTS.debounceMs,
42
+ maxWaitMs: deps.maxWaitMs ?? DEFAULTS.maxWaitMs,
43
+ retryMs: deps.retryMs ?? DEFAULTS.retryMs,
44
+ maxRetryMs: deps.maxRetryMs ?? DEFAULTS.maxRetryMs,
45
+ };
46
+ this.setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
47
+ this.clearTimer = deps.clearTimer ?? ((h) => clearTimeout(h));
48
+ this.now = deps.now ?? Date.now;
49
+ }
50
+ slot(sessionId) {
51
+ let p = this.pending.get(sessionId);
52
+ if (!p) {
53
+ p = { handle: null, firstDirtyAt: 0, running: false, dirtyAgain: false, retryMs: this.cfg.retryMs, stopped: false };
54
+ this.pending.set(sessionId, p);
55
+ }
56
+ return p;
57
+ }
58
+ disarm(p) {
59
+ if (p.handle !== null) {
60
+ this.clearTimer(p.handle);
61
+ p.handle = null;
62
+ }
63
+ }
64
+ /**
65
+ * Work happened; push soon.
66
+ *
67
+ * Cheap and synchronous — this is called from the write path, and the write
68
+ * path must never wait on the mirror.
69
+ */
70
+ touch(sessionId) {
71
+ const p = this.slot(sessionId);
72
+ if (p.stopped)
73
+ return;
74
+ if (p.running) {
75
+ // Do not overlap pushes for one session: the second would race the first's
76
+ // cursor advance and could re-send or skip entries.
77
+ p.dirtyAgain = true;
78
+ return;
79
+ }
80
+ if (!p.firstDirtyAt)
81
+ p.firstDirtyAt = this.now();
82
+ this.disarm(p);
83
+ // The ceiling wins over the quiet period: a busy session must not defer its
84
+ // push forever just because it never goes quiet.
85
+ const elapsed = this.now() - p.firstDirtyAt;
86
+ const wait = Math.max(0, Math.min(this.cfg.debounceMs, this.cfg.maxWaitMs - elapsed));
87
+ p.handle = this.setTimer(() => { void this.run(sessionId); }, wait);
88
+ }
89
+ /**
90
+ * Push now and wait for the result.
91
+ *
92
+ * For pause, handoff and shutdown — the moments where "shortly" is not good
93
+ * enough because the machine may be about to stop being the one that has the
94
+ * session.
95
+ */
96
+ async flush(sessionId) {
97
+ const p = this.slot(sessionId);
98
+ this.disarm(p);
99
+ return this.run(sessionId, { force: true });
100
+ }
101
+ /** Flush every session with work outstanding — shutdown, or a demote of the
102
+ * whole machine. Runs them together; one failure does not stop the others. */
103
+ async flushAll() {
104
+ const ids = [...this.pending.keys()];
105
+ const results = await Promise.all(ids.map(async (id) => [id, await this.flush(id)]));
106
+ return Object.fromEntries(results);
107
+ }
108
+ async run(sessionId, opts = {}) {
109
+ const p = this.slot(sessionId);
110
+ if (p.stopped && !opts.force) {
111
+ return { ok: false, pushed: 0, status: { pushedIndex: 0, lastPushedAt: 0, lastError: 'stopped', authoritative: false, epoch: 0 } };
112
+ }
113
+ p.running = true;
114
+ this.disarm(p);
115
+ try {
116
+ const result = await this.deps.mirror.push(sessionId);
117
+ if (result.lostAuthority) {
118
+ // Another machine has the session. Stop — do not retry, do not re-arm.
119
+ this.deps.log?.(`mirror: authority lost for ${sessionId}; scheduler stopped`);
120
+ p.stopped = true;
121
+ p.dirtyAgain = false;
122
+ p.firstDirtyAt = 0;
123
+ return result;
124
+ }
125
+ if (result.ok) {
126
+ p.retryMs = this.cfg.retryMs;
127
+ p.firstDirtyAt = 0;
128
+ // Work that arrived mid-push has not been sent; schedule another round.
129
+ if (p.dirtyAgain) {
130
+ p.dirtyAgain = false;
131
+ p.running = false;
132
+ this.touch(sessionId);
133
+ return result;
134
+ }
135
+ return result;
136
+ }
137
+ // A transient failure. Back off rather than hammering a server that is
138
+ // already unhappy, and keep the dirty state so nothing is dropped.
139
+ p.handle = this.setTimer(() => { void this.run(sessionId); }, p.retryMs);
140
+ p.retryMs = Math.min(p.retryMs * 2, this.cfg.maxRetryMs);
141
+ return result;
142
+ }
143
+ finally {
144
+ p.running = false;
145
+ }
146
+ }
147
+ /** Whether a push is scheduled or in flight. */
148
+ pending_(sessionId) {
149
+ const p = this.pending.get(sessionId);
150
+ return !!p && (p.handle !== null || p.running || p.dirtyAgain);
151
+ }
152
+ /** Stop scheduling for a session and forget it — a destroy, or a handoff that
153
+ * has already flushed. */
154
+ stop(sessionId) {
155
+ const p = this.pending.get(sessionId);
156
+ if (!p)
157
+ return;
158
+ this.disarm(p);
159
+ this.pending.delete(sessionId);
160
+ }
161
+ dispose() {
162
+ for (const [, p] of this.pending)
163
+ this.disarm(p);
164
+ this.pending.clear();
165
+ }
166
+ }
167
+ //# sourceMappingURL=mirrorScheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirrorScheduler.js","sourceRoot":"","sources":["../../api/mirrorScheduler.ts"],"names":[],"mappings":"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"}
@@ -15,15 +15,4 @@ export interface ConversationTurn {
15
15
  /** Token/cost/latency for the assistant response, once the turn settles. */
16
16
  stats?: TurnStats;
17
17
  }
18
- export declare function getTurns(): ConversationTurn[];
19
- /** React-style setter: accepts a value or an updater. */
20
- export declare function setTurns(next: ConversationTurn[] | ((prev: ConversationTurn[]) => ConversationTurn[])): void;
21
- /** Toggle a turn's include flag (warm-context inclusion — before compression). */
22
- export declare function toggleInclude(id: string): void;
23
- /** Subscribe a component to the live turns. */
24
- export declare function useConversation(): {
25
- turns: ConversationTurn[];
26
- setTurns: typeof setTurns;
27
- toggleInclude: typeof toggleInclude;
28
- };
29
18
  //# sourceMappingURL=conversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../conversation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAEpF,qFAAqF;AACrF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;+EAC+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IACvC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAMD,wBAAgB,QAAQ,IAAI,gBAAgB,EAAE,CAAkB;AAEhE,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,CAAC,GAAG,IAAI,CAG5G;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,IAAI;IACjC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,aAAa,EAAE,OAAO,aAAa,CAAC;CACrC,CAIA"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../conversation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAEpF,qFAAqF;AACrF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;+EAC+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IACvC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB"}
@@ -1,25 +1,2 @@
1
- /* Shared conversation store — the assistant's live turns, lifted out of AgentPanel
2
- * so sibling surfaces (the Context Manager dock panel) can read them and toggle
3
- * include/exclude. Module-level + reactive (same pattern as lib/usage, recall).
4
- * Session load/save still lives in AgentPanel; this is just the live buffer. */
5
- import { useEffect, useReducer } from 'react';
6
- let turns = [];
7
- const listeners = new Set();
8
- const notify = () => { listeners.forEach((fn) => fn()); };
9
- export function getTurns() { return turns; }
10
- /** React-style setter: accepts a value or an updater. */
11
- export function setTurns(next) {
12
- turns = typeof next === 'function' ? next(turns) : next;
13
- notify();
14
- }
15
- /** Toggle a turn's include flag (warm-context inclusion — before compression). */
16
- export function toggleInclude(id) {
17
- setTurns((t) => t.map((x) => (x.id === id ? { ...x, included: !x.included } : x)));
18
- }
19
- /** Subscribe a component to the live turns. */
20
- export function useConversation() {
21
- const [, force] = useReducer((x) => x + 1, 0);
22
- useEffect(() => { listeners.add(force); return () => { listeners.delete(force); }; }, []);
23
- return { turns, setTurns, toggleInclude };
24
- }
1
+ export {};
25
2
  //# sourceMappingURL=conversation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../conversation.ts"],"names":[],"mappings":"AAAA;;;gFAGgF;AAChF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAqB9C,IAAI,KAAK,GAAuB,EAAE,CAAC;AACnC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AACxC,MAAM,MAAM,GAAG,GAAS,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,UAAU,QAAQ,KAAyB,OAAO,KAAK,CAAC,CAAC,CAAC;AAEhE,yDAAyD;AACzD,MAAM,UAAU,QAAQ,CAAC,IAA6E;IACpG,KAAK,GAAG,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAE,IAAsD,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3G,MAAM,EAAE,CAAC;AACX,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,eAAe;IAK7B,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACtD,SAAS,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../conversation.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ export interface RunConstraints {
2
+ maxUsd?: number;
3
+ maxTokens?: number;
4
+ maxMs?: number;
5
+ }
6
+ export interface BudgetState {
7
+ usd: number;
8
+ tokens: number;
9
+ ms: number;
10
+ }
11
+ export interface Breach {
12
+ kind: 'usd' | 'tokens' | 'ms';
13
+ spent: number;
14
+ limit: number;
15
+ }
16
+ export declare class BudgetMeter {
17
+ private readonly constraints;
18
+ private readonly nowFn;
19
+ private usd;
20
+ private tokens;
21
+ private readonly start;
22
+ constructor(constraints: RunConstraints, nowFn: () => number);
23
+ add(usage: {
24
+ inTok?: number;
25
+ outTok?: number;
26
+ cost?: number;
27
+ }): void;
28
+ state(): BudgetState;
29
+ breach(): Breach | null;
30
+ willExceed(projUsd: number, projTokens: number): boolean;
31
+ }
32
+ /** What the agent does when a run budget is breached (the agent-surface
33
+ * vocabulary; the strategy runner has its own ladder/degrade/replan/stop). */
34
+ export type AgentBreachAction = 'ask' | 'stop' | 'warn' | 'degrade';
35
+ /** RunConstraints plus the agent's breach policy — the shape every agent
36
+ * surface persists and every runtime merges per run. */
37
+ export interface AgentConstraints extends RunConstraints {
38
+ onBreach?: AgentBreachAction;
39
+ }
40
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../core/budget.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AACvF,MAAM,WAAW,WAAW;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AACxE,MAAM,WAAW,MAAM;IAAG,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAEvF,qBAAa,WAAW;IAMpB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IANxB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAGZ,WAAW,EAAE,cAAc,EAC3B,KAAK,EAAE,MAAM,MAAM;IAKtC,GAAG,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKpE,KAAK,IAAI,WAAW;IAIpB,MAAM,IAAI,MAAM,GAAG,IAAI;IASvB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;CAMzD;AAED;8EAC8E;AAC9E,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AACpE;wDACwD;AACxD,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IAAG,QAAQ,CAAC,EAAE,iBAAiB,CAAA;CAAE"}
@@ -0,0 +1,62 @@
1
+ export class BudgetMeter {
2
+ constructor(constraints, nowFn) {
3
+ Object.defineProperty(this, "constraints", {
4
+ enumerable: true,
5
+ configurable: true,
6
+ writable: true,
7
+ value: constraints
8
+ });
9
+ Object.defineProperty(this, "nowFn", {
10
+ enumerable: true,
11
+ configurable: true,
12
+ writable: true,
13
+ value: nowFn
14
+ });
15
+ Object.defineProperty(this, "usd", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: 0
20
+ });
21
+ Object.defineProperty(this, "tokens", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: 0
26
+ });
27
+ Object.defineProperty(this, "start", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: void 0
32
+ });
33
+ this.start = nowFn();
34
+ }
35
+ add(usage) {
36
+ this.tokens += (usage.inTok || 0) + (usage.outTok || 0);
37
+ this.usd += usage.cost || 0;
38
+ }
39
+ state() {
40
+ return { usd: this.usd, tokens: this.tokens, ms: this.nowFn() - this.start };
41
+ }
42
+ breach() {
43
+ const { maxUsd, maxTokens, maxMs } = this.constraints;
44
+ const s = this.state();
45
+ if (maxUsd != null && s.usd > maxUsd)
46
+ return { kind: 'usd', spent: s.usd, limit: maxUsd };
47
+ if (maxTokens != null && s.tokens > maxTokens)
48
+ return { kind: 'tokens', spent: s.tokens, limit: maxTokens };
49
+ if (maxMs != null && s.ms > maxMs)
50
+ return { kind: 'ms', spent: s.ms, limit: maxMs };
51
+ return null;
52
+ }
53
+ willExceed(projUsd, projTokens) {
54
+ const { maxUsd, maxTokens } = this.constraints;
55
+ if (maxUsd != null && this.usd + projUsd > maxUsd)
56
+ return true;
57
+ if (maxTokens != null && this.tokens + projTokens > maxTokens)
58
+ return true;
59
+ return false;
60
+ }
61
+ }
62
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../core/budget.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,WAAW;IAKtB,YACmB,WAA2B,EAC3B,KAAmB;QADpC;;;;mBAAiB,WAAW;WAAgB;QAC5C;;;;mBAAiB,KAAK;WAAc;QAN9B;;;;mBAAM,CAAC;WAAC;QACR;;;;mBAAS,CAAC;WAAC;QACF;;;;;WAAc;QAM7B,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,GAAG,CAAC,KAAyD;QAC3D,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK;QACH,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC/E,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QACtD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,MAAM,IAAI,IAAI,IAAI,CAAC,CAAC,GAAG,GAAG,MAAM;YAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC1F,IAAI,SAAS,IAAI,IAAI,IAAI,CAAC,CAAC,MAAM,GAAG,SAAS;YAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAC5G,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,EAAE,GAAG,KAAK;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACpF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,OAAe,EAAE,UAAkB;QAC5C,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QAC/C,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,GAAG,OAAO,GAAG,MAAM;YAAE,OAAO,IAAI,CAAC;QAC/D,IAAI,SAAS,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,UAAU,GAAG,SAAS;YAAE,OAAO,IAAI,CAAC;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -1,12 +1,16 @@
1
1
  import type OpenAI from 'openai';
2
2
  import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
3
3
  import type { AgentTool, ToolContext, AgentEvent, Usage } from '@corenel/protocol';
4
+ import { type RecoveryConfig } from './recovery';
4
5
  import type { Policy } from '@corenel/protocol';
5
6
  import type { PermissionService } from '../guardrail/permission-service';
6
7
  import { type CompressionConfig } from '../compression';
7
8
  export interface RunAgentArgs {
8
9
  client: OpenAI;
9
10
  model: string;
11
+ /** Resolve the model fresh each turn — lets the host degrade to a cheaper
12
+ * model mid-run. Omit for a fixed `model`. */
13
+ getModel?: () => string;
10
14
  system?: string;
11
15
  messages: ChatCompletionMessageParam[];
12
16
  tools: AgentTool[];
@@ -18,6 +22,13 @@ export interface RunAgentArgs {
18
22
  permission?: PermissionService;
19
23
  /** Context-compression config (rung-1 offload). Omit to disable. */
20
24
  compression?: CompressionConfig;
25
+ /** Intra-run self-healing budget (stream-retry + tool-steer). Omit for defaults. */
26
+ recovery?: Partial<RecoveryConfig>;
27
+ /** Optional output-token ceiling forwarded to the model on every turn (reasoning
28
+ * + visible output combined for reasoning models). Omit to leave output unbounded. */
29
+ maxCompletionTokens?: number;
30
+ /** Sampling temperature, already vetted by the caller for model support. */
31
+ temperature?: number;
21
32
  signal?: AbortSignal;
22
33
  onEvent?: (e: AgentEvent) => void;
23
34
  }