agent-nuvira 3.1.3 → 3.3.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 (164) hide show
  1. package/dist/agents/agents/reasoner.d.ts +8 -0
  2. package/dist/agents/agents/reasoner.d.ts.map +1 -1
  3. package/dist/agents/agents/reasoner.js +53 -2
  4. package/dist/agents/agents/reasoner.js.map +1 -1
  5. package/dist/agents/agents/writer.d.ts +24 -0
  6. package/dist/agents/agents/writer.d.ts.map +1 -1
  7. package/dist/agents/agents/writer.js +200 -0
  8. package/dist/agents/agents/writer.js.map +1 -1
  9. package/dist/agents/composite-plan.d.ts +143 -0
  10. package/dist/agents/composite-plan.d.ts.map +1 -0
  11. package/dist/agents/composite-plan.js +399 -0
  12. package/dist/agents/composite-plan.js.map +1 -0
  13. package/dist/agents/long-form-plan.d.ts +156 -0
  14. package/dist/agents/long-form-plan.d.ts.map +1 -0
  15. package/dist/agents/long-form-plan.js +274 -0
  16. package/dist/agents/long-form-plan.js.map +1 -0
  17. package/dist/agents/orchestrator.d.ts +107 -0
  18. package/dist/agents/orchestrator.d.ts.map +1 -1
  19. package/dist/agents/orchestrator.js +501 -35
  20. package/dist/agents/orchestrator.js.map +1 -1
  21. package/dist/agents/prompt-assembly.d.ts +8 -0
  22. package/dist/agents/prompt-assembly.d.ts.map +1 -1
  23. package/dist/agents/prompt-assembly.js +17 -0
  24. package/dist/agents/prompt-assembly.js.map +1 -1
  25. package/dist/cli/chat.d.ts +33 -0
  26. package/dist/cli/chat.d.ts.map +1 -1
  27. package/dist/cli/chat.js +161 -7
  28. package/dist/cli/chat.js.map +1 -1
  29. package/dist/cli/execute.d.ts +12 -0
  30. package/dist/cli/execute.d.ts.map +1 -1
  31. package/dist/cli/execute.js +150 -2
  32. package/dist/cli/execute.js.map +1 -1
  33. package/dist/cli/loop-executor.d.ts +17 -0
  34. package/dist/cli/loop-executor.d.ts.map +1 -1
  35. package/dist/cli/loop-executor.js +160 -1
  36. package/dist/cli/loop-executor.js.map +1 -1
  37. package/dist/cli/models.d.ts.map +1 -1
  38. package/dist/cli/models.js +10 -0
  39. package/dist/cli/models.js.map +1 -1
  40. package/dist/cli/trace.d.ts +19 -0
  41. package/dist/cli/trace.d.ts.map +1 -1
  42. package/dist/cli/trace.js +109 -2
  43. package/dist/cli/trace.js.map +1 -1
  44. package/dist/gateway/registry.d.ts +31 -0
  45. package/dist/gateway/registry.d.ts.map +1 -1
  46. package/dist/gateway/registry.js +162 -13
  47. package/dist/gateway/registry.js.map +1 -1
  48. package/dist/inference/model-entitlement.d.ts +46 -0
  49. package/dist/inference/model-entitlement.d.ts.map +1 -0
  50. package/dist/inference/model-entitlement.js +98 -0
  51. package/dist/inference/model-entitlement.js.map +1 -0
  52. package/dist/inference/tool-call-utils.d.ts +10 -0
  53. package/dist/inference/tool-call-utils.d.ts.map +1 -1
  54. package/dist/inference/tool-call-utils.js +90 -0
  55. package/dist/inference/tool-call-utils.js.map +1 -1
  56. package/dist/learning/autonomy-policy.d.ts +370 -0
  57. package/dist/learning/autonomy-policy.d.ts.map +1 -0
  58. package/dist/learning/autonomy-policy.js +544 -0
  59. package/dist/learning/autonomy-policy.js.map +1 -0
  60. package/dist/learning/cost-tracker.d.ts +14 -0
  61. package/dist/learning/cost-tracker.d.ts.map +1 -1
  62. package/dist/learning/cost-tracker.js +22 -0
  63. package/dist/learning/cost-tracker.js.map +1 -1
  64. package/dist/learning/credential-fingerprint.d.ts +58 -0
  65. package/dist/learning/credential-fingerprint.d.ts.map +1 -0
  66. package/dist/learning/credential-fingerprint.js +126 -0
  67. package/dist/learning/credential-fingerprint.js.map +1 -0
  68. package/dist/learning/deliverable-class.d.ts +178 -0
  69. package/dist/learning/deliverable-class.d.ts.map +1 -0
  70. package/dist/learning/deliverable-class.js +504 -0
  71. package/dist/learning/deliverable-class.js.map +1 -0
  72. package/dist/learning/engine-router.d.ts +12 -1
  73. package/dist/learning/engine-router.d.ts.map +1 -1
  74. package/dist/learning/engine-router.js +28 -0
  75. package/dist/learning/engine-router.js.map +1 -1
  76. package/dist/learning/long-form.d.ts +252 -0
  77. package/dist/learning/long-form.d.ts.map +1 -0
  78. package/dist/learning/long-form.js +521 -0
  79. package/dist/learning/long-form.js.map +1 -0
  80. package/dist/learning/model-first-router.d.ts +17 -0
  81. package/dist/learning/model-first-router.d.ts.map +1 -1
  82. package/dist/learning/model-first-router.js +27 -0
  83. package/dist/learning/model-first-router.js.map +1 -1
  84. package/dist/learning/model-registry.d.ts +66 -4
  85. package/dist/learning/model-registry.d.ts.map +1 -1
  86. package/dist/learning/model-registry.js +67 -6
  87. package/dist/learning/model-registry.js.map +1 -1
  88. package/dist/learning/model-warmup.d.ts +97 -2
  89. package/dist/learning/model-warmup.d.ts.map +1 -1
  90. package/dist/learning/model-warmup.js +165 -60
  91. package/dist/learning/model-warmup.js.map +1 -1
  92. package/dist/learning/prompt-layers.d.ts +61 -0
  93. package/dist/learning/prompt-layers.d.ts.map +1 -0
  94. package/dist/learning/prompt-layers.js +140 -0
  95. package/dist/learning/prompt-layers.js.map +1 -0
  96. package/dist/learning/provider-limits.d.ts +66 -0
  97. package/dist/learning/provider-limits.d.ts.map +1 -0
  98. package/dist/learning/provider-limits.js +184 -0
  99. package/dist/learning/provider-limits.js.map +1 -0
  100. package/dist/learning/reasoning-trace.d.ts +125 -1
  101. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  102. package/dist/learning/reasoning-trace.js +85 -2
  103. package/dist/learning/reasoning-trace.js.map +1 -1
  104. package/dist/learning/resilient-call.d.ts +36 -1
  105. package/dist/learning/resilient-call.d.ts.map +1 -1
  106. package/dist/learning/resilient-call.js +80 -5
  107. package/dist/learning/resilient-call.js.map +1 -1
  108. package/dist/learning/unattended-job.d.ts +350 -0
  109. package/dist/learning/unattended-job.d.ts.map +1 -0
  110. package/dist/learning/unattended-job.js +636 -0
  111. package/dist/learning/unattended-job.js.map +1 -0
  112. package/dist/learning/unattended-progress.d.ts +95 -0
  113. package/dist/learning/unattended-progress.d.ts.map +1 -0
  114. package/dist/learning/unattended-progress.js +147 -0
  115. package/dist/learning/unattended-progress.js.map +1 -0
  116. package/dist/learning/working-state.d.ts +109 -0
  117. package/dist/learning/working-state.d.ts.map +1 -0
  118. package/dist/learning/working-state.js +244 -0
  119. package/dist/learning/working-state.js.map +1 -0
  120. package/dist/nlu/conversation-gate.d.ts +33 -0
  121. package/dist/nlu/conversation-gate.d.ts.map +1 -1
  122. package/dist/nlu/conversation-gate.js +68 -3
  123. package/dist/nlu/conversation-gate.js.map +1 -1
  124. package/dist/tools/coding-tools.d.ts.map +1 -1
  125. package/dist/tools/coding-tools.js +95 -19
  126. package/dist/tools/coding-tools.js.map +1 -1
  127. package/dist/tools/edit-verification.d.ts +142 -0
  128. package/dist/tools/edit-verification.d.ts.map +1 -0
  129. package/dist/tools/edit-verification.js +262 -0
  130. package/dist/tools/edit-verification.js.map +1 -0
  131. package/dist/tools/git-tool.d.ts.map +1 -1
  132. package/dist/tools/git-tool.js +37 -4
  133. package/dist/tools/git-tool.js.map +1 -1
  134. package/dist/tools/registry.d.ts +30 -2
  135. package/dist/tools/registry.d.ts.map +1 -1
  136. package/dist/tools/registry.js +40 -11
  137. package/dist/tools/registry.js.map +1 -1
  138. package/dist/tools/run-cli.d.ts.map +1 -1
  139. package/dist/tools/run-cli.js +40 -13
  140. package/dist/tools/run-cli.js.map +1 -1
  141. package/dist/tools/run-terminal.d.ts +6 -1
  142. package/dist/tools/run-terminal.d.ts.map +1 -1
  143. package/dist/tools/run-terminal.js +158 -18
  144. package/dist/tools/run-terminal.js.map +1 -1
  145. package/dist/tools/tool-loop.d.ts +109 -0
  146. package/dist/tools/tool-loop.d.ts.map +1 -1
  147. package/dist/tools/tool-loop.js +362 -2
  148. package/dist/tools/tool-loop.js.map +1 -1
  149. package/dist/web-dashboard/chat-console.d.ts +8 -0
  150. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  151. package/dist/web-dashboard/chat-console.js +9 -0
  152. package/dist/web-dashboard/chat-console.js.map +1 -1
  153. package/dist/web-dashboard/chat-retry.d.ts.map +1 -1
  154. package/dist/web-dashboard/chat-retry.js +10 -2
  155. package/dist/web-dashboard/chat-retry.js.map +1 -1
  156. package/dist/web-dashboard/server.d.ts.map +1 -1
  157. package/dist/web-dashboard/server.js +80 -8
  158. package/dist/web-dashboard/server.js.map +1 -1
  159. package/dist/web-dashboard/src/types.d.ts +81 -4
  160. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  161. package/package.json +1 -1
  162. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js → index-kCUkORm7.js} +2 -2
  163. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js.map → index-kCUkORm7.js.map} +1 -1
  164. package/src/web-dashboard/public/index.html +1 -1
@@ -0,0 +1,544 @@
1
+ /**
2
+ * Autonomy policy — may the agent decide for itself, or must it ask?
3
+ * (enterprise-grade hardening, G11.)
4
+ *
5
+ * WHY THIS EXISTS (the WhatsApp story audit + the "manual cadence" complaint):
6
+ * Two failure modes sit on opposite sides of the same missing rule.
7
+ *
8
+ * 1. STALLING ON AN ANSWERABLE QUESTION. The story session asked/implied
9
+ * decisions it could have made itself ("should I keep going?", "which
10
+ * file?"), and every one of those turned into a turn that ended without
11
+ * delivered work. An agent that consults on a choice it can make is not
12
+ * careful — it is a bottleneck, and it is how 32 minutes produced zero
13
+ * words.
14
+ * 2. DECIDING SOMETHING THAT WAS NEVER ITS CALL. The opposite failure, where
15
+ * an agent silently picks a direction the user cared about (which
16
+ * provider to bill, whether to overwrite existing work, what to delete).
17
+ *
18
+ * The enterprise behaviour is a POLICY, not a mood: decide by default, consult
19
+ * only when the decision is (a) genuinely the user's, (b) not implied by the
20
+ * ask, (c) irreversible or expensive to get wrong, and (d) has no sensible
21
+ * default. Everything else is the agent's job.
22
+ *
23
+ * This module is deterministic and LLM-free on purpose: it is the guard rail
24
+ * around a model's own "should I ask?" instinct, which skews towards asking.
25
+ */
26
+ /**
27
+ * Rework above this many minutes makes a defaulted choice expensive enough to
28
+ * be worth a round trip even when the choice is reversible.
29
+ */
30
+ export const REWORK_CONSULT_THRESHOLD_MINUTES = 45;
31
+ /**
32
+ * Decide whether the agent proceeds on its own or asks the user.
33
+ *
34
+ * Order matters. The cheap, obviously-ours cases are settled first so that a
35
+ * later rule can never turn an easy call into a consultation:
36
+ *
37
+ * 1. NOT BLOCKING, NOT HIGH-IMPACT -> proceed. Nothing is waiting on an
38
+ * answer, and the user can redirect at any time. Asking is pure latency.
39
+ * 2. THE ASK ALREADY SAID SO -> proceed with the implied choice. Re-asking a
40
+ * question the user answered in their own request is the "cheap shortcut"
41
+ * in reverse: it looks like diligence and delivers nothing.
42
+ * 3. A BLOCKING, HIGH-IMPACT, IRREVERSIBLE DECISION WITH NO DEFAULT -> this is
43
+ * the user's call. Consult — with a defaulted recommendation, so the reply
44
+ * is a one-word confirmation rather than an essay.
45
+ * 4. OTHERWISE -> proceed with the default, unless the rework a wrong pick
46
+ * would cost is large enough to justify asking (rule 5).
47
+ * 5. EXPENSIVE + NO DEFAULT -> consult.
48
+ *
49
+ * A blocking decision with a sensible default proceeds. That is the whole
50
+ * point of the policy: "which file should I write to" has a default (the one
51
+ * named in the ask, or a predictable one), so it must never stop a run.
52
+ */
53
+ export function decideAutonomously(request) {
54
+ const impact = request.impact ?? 'medium';
55
+ const blocking = request.blocking === true;
56
+ const choice = request.defaultChoice ?? request.options?.[0];
57
+ if (!blocking && impact !== 'high') {
58
+ return {
59
+ action: 'proceed',
60
+ choice,
61
+ reason: 'non-blocking and not high-impact — proceeding and leaving the user free to redirect',
62
+ };
63
+ }
64
+ if (request.impliedByAsk) {
65
+ return {
66
+ action: 'proceed',
67
+ choice,
68
+ reason: 'the request itself already specifies this — re-asking would be a round trip with no new information',
69
+ };
70
+ }
71
+ const irreversible = request.reversible === false;
72
+ const rework = request.reworkMinutes ?? 0;
73
+ const hasDefault = typeof choice === 'string' && choice.length > 0;
74
+ if (blocking && impact === 'high' && irreversible && !hasDefault) {
75
+ return {
76
+ action: 'consult',
77
+ reason: 'blocking, high-impact, irreversible, and no sensible default — this is genuinely the user’s decision',
78
+ };
79
+ }
80
+ if (hasDefault && !(irreversible && impact === 'high')) {
81
+ return {
82
+ action: 'proceed',
83
+ choice,
84
+ reason: 'a sensible default exists and the choice is recoverable — deciding it is the agent’s job',
85
+ };
86
+ }
87
+ if (rework > REWORK_CONSULT_THRESHOLD_MINUTES && !hasDefault) {
88
+ return {
89
+ action: 'consult',
90
+ reason: `a wrong pick would cost roughly ${rework} minutes and there is no default to fall back on`,
91
+ };
92
+ }
93
+ if (!hasDefault && request.blocking) {
94
+ return {
95
+ action: 'consult',
96
+ reason: 'blocking, with no default the agent is willing to stand behind',
97
+ };
98
+ }
99
+ return {
100
+ action: 'proceed',
101
+ choice,
102
+ reason: irreversible && impact === 'high'
103
+ ? 'proceeding with the stated default; the choice is high-impact but the user’s own preference is already known'
104
+ : 'no reason to wait on an answer — proceeding',
105
+ };
106
+ }
107
+ // ── Write authorization: did the REQUEST already authorize this? ────────────
108
+ /**
109
+ * Verbs that mean "produce the artifact", not "tell me about it".
110
+ * Deliberately includes the edit verbs: "add a chapter", "update the README".
111
+ */
112
+ const CREATE_VERB_RE = /\b(?:create|build|write|make|generate|develop|implement|scaffold|set\s+up|setup|add|produce|draft|code|rewrite|refactor|fix|update|change|edit|modify|migrate|upgrade|convert|render|export|deploy|install|save|store|persist)\b/i;
113
+ /** Nouns that name a FILE-SHAPED deliverable (the thing that lands on disk). */
114
+ const DELIVERABLE_NOUN_RE = /\b(?:file|files|folder|directory|project|repo|repository|app|application|website|site|webpage|web\s?page|page|pages|component|module|class|function|script|program|tool|cli|server|api|endpoint|test|tests|suite|config|document|doc|docs|report|readme|story|book|novel|chapter|chapters|poem|essay|article|post|plan|spec|schema|migration|template|templates|style|styles|asset|assets|manuscript|draft)\b/i;
115
+ /** A path is the strongest signal of all: the user named the destination. */
116
+ const PATH_RE = /(?:^|[\s"'`(])(?:~|\/|\.{1,2}\/)[\w./-]+/;
117
+ /**
118
+ * Directive verbs that ask for a CHANGE to something that already exists.
119
+ *
120
+ * Kept separate from {@link CREATE_VERB_RE} because the evidence is different:
121
+ * these need no file-shaped noun ("fix the calculator") — the verb applied to an
122
+ * object already says the work is to change what is there. Still vetoed by an
123
+ * analysis opener, so "why is the build failing?" remains a question.
124
+ */
125
+ const MAINTENANCE_VERB_RE = /\b(?:fix|repair|refactor|update|change|edit|modify|migrate|upgrade|improve|optimize|optimise|clean\s*up|rewrite|rename|remove|delete|correct|debug|patch|tweak)\b/i;
126
+ /**
127
+ * An analysis/interrogative opener means the user is asking ABOUT something,
128
+ * not asking for it to be produced: "what files should I create for X?" is a
129
+ * question, "create the files for X" is a request.
130
+ *
131
+ * `can|could|would|do` are deliberately NOT here — "Can you create the
132
+ * project?" is a polite REQUEST, and treating it as analysis would leave the
133
+ * most common phrasing unauthorized.
134
+ */
135
+ const ANALYSIS_OPENER_RE = /^\s*(?:how|why|what|when|which|who|whom|whose|explain|describe|summari[sz]e|compare|tell\s+me|walk\s+me|help\s+me\s+understand)\b/i;
136
+ /** "yes", "go ahead", "do it" — a confirmation of work already proposed. */
137
+ const SHORT_AFFIRMATIVE_RE = /^\s*(?:yes|yep|yeah|ok(?:ay)?|sure|please\s+do|go\s+ahead|proceed|continue|carry\s+on|do\s+it|go\s+for\s+it|make\s+it\s+so)\b/i;
138
+ /** Continuing in-flight work needs no new authorization — the ask was it. */
139
+ const CONTINUATION_RE = /\b(?:continue|carry\s+on|keep\s+going|go\s+ahead|proceed|finish|complete\s+(?:it|the|this|the\s+remaining)|do\s+the\s+remaining|resume)\b/i;
140
+ /**
141
+ * Does this request authorize the agent to CREATE files?
142
+ *
143
+ * This is the input the write gate was missing. The gate was binary — "confirm
144
+ * or refuse" — with no notion of a request that had ALREADY authorized the
145
+ * work, so an unattended run whose ask was literally "write a 12 page story"
146
+ * stopped to ask "Do you want me to create the files?". That is not caution;
147
+ * it is the manual cadence, and over a chat surface it is a round trip that
148
+ * produces nothing.
149
+ *
150
+ * Evidence order: an explicit confirmation of proposed work, then a
151
+ * continuation, then a named destination path, then a creation verb applied to
152
+ * a file-shaped deliverable. An analysis/interrogative opener vetoes the last
153
+ * two, so "explain how to write a story to a file" stays a question.
154
+ *
155
+ * Deliberately conservative: anything it does not recognise is NOT authorized,
156
+ * which keeps today's confirm-or-refuse gate exactly as strict as it was.
157
+ */
158
+ export function requestAuthorizesWrites(request) {
159
+ const text = (request || '').trim();
160
+ if (!text)
161
+ return { authorized: false, reason: 'no request text to judge' };
162
+ if (text.length <= 40 && SHORT_AFFIRMATIVE_RE.test(text)) {
163
+ return { authorized: true, reason: 'the user confirmed the work already proposed' };
164
+ }
165
+ if (CONTINUATION_RE.test(text)) {
166
+ return { authorized: true, reason: 'the request continues work that was already authorized' };
167
+ }
168
+ const analyzing = ANALYSIS_OPENER_RE.test(text);
169
+ const namedPath = extractRequestedPath(text);
170
+ if (namedPath && CREATE_VERB_RE.test(text) && !analyzing) {
171
+ return {
172
+ authorized: true,
173
+ reason: 'the request names a destination path for the work',
174
+ requestedPath: namedPath,
175
+ };
176
+ }
177
+ if (analyzing) {
178
+ return { authorized: false, reason: 'the request asks ABOUT the work rather than for it to be produced' };
179
+ }
180
+ if (CREATE_VERB_RE.test(text) && DELIVERABLE_NOUN_RE.test(text)) {
181
+ return { authorized: true, reason: 'the request asks for a file-shaped deliverable' };
182
+ }
183
+ // The noun list names ARTIFACT TYPES (file, app, story, script…), so it misses
184
+ // the most common dev ask there is: "fix the calculator" names no artifact at
185
+ // all, and this verdict came back NOT authorized — which meant the edit gate
186
+ // would still stop to ask for permission to fix the thing the user asked to
187
+ // have fixed. A directive verb is its own evidence: it asks for a change to
188
+ // work that already exists.
189
+ if (MAINTENANCE_VERB_RE.test(text)) {
190
+ return { authorized: true, reason: 'the request directs a change to work that already exists' };
191
+ }
192
+ return { authorized: false, reason: 'the request does not ask for files to be created' };
193
+ }
194
+ /**
195
+ * Pull the destination path out of a request, when it names one.
196
+ *
197
+ * Uses the same pattern the authorization verdict keys on, so "the request
198
+ * names a path" and "here is that path" can never disagree — the failure mode
199
+ * being a gate that refuses for lack of a path while the request visibly
200
+ * contains one. Returns the trimmed match with trailing sentence punctuation
201
+ * removed ("…at /path/x.md." is the file `/path/x.md`).
202
+ */
203
+ export function extractRequestedPath(request) {
204
+ const text = (request || '').trim();
205
+ if (!text)
206
+ return undefined;
207
+ const match = text.match(PATH_RE);
208
+ const raw = match?.[0]?.trim();
209
+ if (!raw)
210
+ return undefined;
211
+ // PATH_RE starts on a boundary character (whitespace/quote/paren); drop it.
212
+ const path = raw.replace(/^[\s"'`(]+/, '').replace(/[.,;:!?)\]]+$/, '').trim();
213
+ return path.length > 0 ? path : undefined;
214
+ }
215
+ /**
216
+ * A request that asks for the work to be recorded in git history.
217
+ *
218
+ * `git commit` was gated behind `confirm:true` "after the user approved via
219
+ * ask_user" — but when the user's ask IS "commit these changes", they have
220
+ * already approved and the gate is a round trip in the most common dev flow.
221
+ * Like every other rule here, the evidence has to come from the request text.
222
+ *
223
+ * A NEGATED commit ("don't commit yet") is explicitly not a request for one,
224
+ * and an analysis opener ("how do I commit?") is asking ABOUT it.
225
+ */
226
+ export function requestRequestsCommit(request) {
227
+ const text = (request || '').trim();
228
+ if (!text)
229
+ return false;
230
+ if (ANALYSIS_OPENER_RE.test(text))
231
+ return false;
232
+ if (/\b(?:don'?t|do\s+not|never|no|without)\s+(?:\w+\s+){0,2}commit\b/i.test(text))
233
+ return false;
234
+ return /\b(?:commit|check[\s-]?in)\b/i.test(text);
235
+ }
236
+ /** Escape a literal for use inside a RegExp. */
237
+ function escapeRegexLiteral(text) {
238
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
239
+ }
240
+ /**
241
+ * Does the request name THIS file?
242
+ *
243
+ * The evidence an edit gate needs: "fix calc.ts" names the file, "update the
244
+ * README" names it. Compared on the BASENAME too, because a request says
245
+ * `calc.ts` for `src/lib/calc.ts` — that is how people actually write, and
246
+ * requiring the full path would make the rule dead on arrival.
247
+ *
248
+ * Deliberately literal (the basename or the path, as a whole word): a fuzzy
249
+ * match would let "write a story" claim `story.md` and turn a create into an
250
+ * unreviewed overwrite.
251
+ */
252
+ export function requestNamesPath(request, path) {
253
+ const text = (request || '').trim();
254
+ if (!text)
255
+ return false;
256
+ const base = path.split(/[\\/]/).pop() ?? '';
257
+ const candidates = [base, path].filter((c) => c.length >= 3);
258
+ return candidates.some((c) => new RegExp(`(?:^|[^\\w.\\/-])${escapeRegexLiteral(c)}(?:$|[^\\w.\\/-])`, 'i').test(text));
259
+ }
260
+ /**
261
+ * Above this share of a file being rewritten, an edit is a REPLACEMENT rather
262
+ * than a surgical change — and replacing most of what exists is the user's call,
263
+ * for the same reason write_file's overwrite case is.
264
+ */
265
+ export const EDIT_SURGICAL_MAX_FRACTION = 0.5;
266
+ /**
267
+ * Is this edit surgical — does it preserve most of the file it touches?
268
+ *
269
+ * The measurable line between "a fix inside a file" and "a rewrite of the
270
+ * file". A surgical edit is the iteration the verify loop is built around
271
+ * (run → read the failure → edit → re-run); a rewrite is a judgment call.
272
+ */
273
+ export function isSurgicalEdit(fileChars, oldChars, newChars) {
274
+ if (!Number.isFinite(fileChars) || fileChars <= 0)
275
+ return false;
276
+ const touched = Math.max(oldChars, newChars);
277
+ return touched / fileChars <= EDIT_SURGICAL_MAX_FRACTION;
278
+ }
279
+ // ── Permission-seeking detection ────────────────────────────────────────────
280
+ /** Direct permission requests: "do you want me to …", "shall I …". */
281
+ const PERMISSION_ASK_RE = /\b(?:do\s+you\s+want\s+(?:me|for\s+me)\s+to|would\s+you\s+like\s+(?:me|for\s+me)\s+to|want\s+me\s+to|like\s+me\s+to|shall\s+i|should\s+i|may\s+i|am\s+i\s+allowed\s+to|is\s+it\s+ok(?:ay)?\s+(?:if|to)|do\s+you\s+want\s+me\s+to|ready\s+to\s+proceed|ok(?:ay)?\s+to\s+proceed)\b/i;
282
+ /** Awaiting-a-human phrasings: "awaiting your confirmation", "let me know if…". */
283
+ const PERMISSION_AWAIT_RE = /\b(?:awaiting|waiting\s+for|need)\s+(?:your|the\s+user'?s?)\s+(?:approval|confirmation|go-?ahead|permission|sign-?off|consent|green\s*light)\b|\b(?:your|the\s+user'?s?)\s+(?:approval|confirmation|go-?ahead|permission|sign-?off)\b|\b(?:if|once|when)\s+you\s+(?:approve|confirm|give\s+me\s+the\s+(?:go-?ahead|green\s*light|ok))\b|\blet\s+me\s+know\s+(?:if|whether)\s+you(?:'d|\s+would)?\s+(?:like|want)\b|\bplease\s+(?:confirm|approve)\b/i;
284
+ /**
285
+ * A question that PROPOSES doing the work rather than asking about it —
286
+ * "Create the Mahagatha interactive-book project with 20 chapter pages…?"
287
+ * (the exact shape that ended a live unattended turn).
288
+ */
289
+ const PROPOSAL_QUESTION_RE = /\?\s*$/;
290
+ /** Sentence split that keeps the shape of the original text for callers that
291
+ * need to CUT rather than merely match. */
292
+ function splitSentences(text) {
293
+ return text
294
+ .split(/(?<=[.!?。!?])\s+|\n+/)
295
+ .map((s) => s.trim())
296
+ .filter(Boolean);
297
+ }
298
+ /** Is this ONE sentence a request for permission / a proposal to do the work? */
299
+ export function isPermissionSeekingSentence(sentence) {
300
+ const s = (sentence || '').trim();
301
+ if (!s)
302
+ return false;
303
+ if (PERMISSION_ASK_RE.test(s) || PERMISSION_AWAIT_RE.test(s))
304
+ return true;
305
+ // A question that proposes CREATE_VERB + DELIVERABLE is asking WHETHER to do
306
+ // the work, not asking about it (the "Create the Mahagatha project…?" shape).
307
+ return PROPOSAL_QUESTION_RE.test(s) && CREATE_VERB_RE.test(s) && DELIVERABLE_NOUN_RE.test(s);
308
+ }
309
+ /**
310
+ * True when the answer CLOSES by asking permission to do work, rather than
311
+ * delivering it.
312
+ *
313
+ * The distinction that matters: "should I proceed?" is a stall, while "which
314
+ * title do you prefer?" is a genuine question. This detector only claims the
315
+ * permission-seeking half — callers AND it with an authorization verdict, so a
316
+ * question about work that was never authorized is untouched.
317
+ *
318
+ * Only the closing sentences are judged: a question mid-answer followed by the
319
+ * actual deliverable is narration, not a stall.
320
+ */
321
+ export function detectPermissionSeeking(content) {
322
+ const text = (content || '').trim();
323
+ if (!text)
324
+ return false;
325
+ return splitSentences(text).slice(-2).some(isPermissionSeekingSentence);
326
+ }
327
+ /**
328
+ * Does the reply END by asking the reader something?
329
+ *
330
+ * The companion question to {@link detectPermissionSeeking}, and the difference
331
+ * matters for the DELIVERABLE gate: "Do you want me to create the files?" is a
332
+ * stall the agent must decide past, while "Which of these two titles do you
333
+ * prefer?" is a decision input the user asked to be consulted on. Treating them
334
+ * alike would either stall on the first or bulldoze the second.
335
+ *
336
+ * Only the closing is judged (the gate's window is the turn's END), and only a
337
+ * literal question — an answer that merely mentions questions mid-text is
338
+ * finished prose.
339
+ */
340
+ export function replyAsksTheReader(content) {
341
+ const sentences = splitSentences((content || '').trim());
342
+ const last = sentences[sentences.length - 1];
343
+ return Boolean(last && last.trim().endsWith('?'));
344
+ }
345
+ /**
346
+ * Remove the trailing permission-seeking sentences from an answer, keeping
347
+ * everything before them VERBATIM (deliverables are prose — re-joining split
348
+ * sentences would reformat the work itself).
349
+ *
350
+ * Used when a turn is nudged to proceed: the real work in that step must stay
351
+ * a candidate answer, but the question around it must not be what the user is
352
+ * handed — otherwise a longer "…Do you want me to…?" outranks a shorter, real
353
+ * follow-up answer under the loop's longest-substantive rule.
354
+ */
355
+ export function stripTrailingPermissionSeek(content) {
356
+ let out = (content || '').trim();
357
+ // Two sentences is the detection window — never cut deeper than it can see.
358
+ for (let i = 0; i < 2; i += 1) {
359
+ const sentences = splitSentences(out);
360
+ const last = sentences[sentences.length - 1];
361
+ if (!last || !isPermissionSeekingSentence(last))
362
+ break;
363
+ const at = out.lastIndexOf(last);
364
+ if (at <= 0)
365
+ return '';
366
+ out = out.slice(0, at).trim();
367
+ }
368
+ return out;
369
+ }
370
+ /**
371
+ * Actions that cannot be undone by re-running the agent. A question that names
372
+ * one of these is NEVER treated as reflexive permission-seeking, because the
373
+ * user genuinely has to own it.
374
+ */
375
+ export const IRREVERSIBLE_ACTION_RE = /\b(?:delete|remove|erase|overwrite|replace\s+(?:the\s+)?existing|publish|deploy|push|force|reset|drop|truncate|uninstall|wipe|send|email|charge|pay|bill|refund|order|purchase)\b/i;
376
+ /** Decide whether a state-changing write proceeds or asks the user. */
377
+ export function decideWriteConfirmation(request) {
378
+ if (!request.authorizedByRequest) {
379
+ return {
380
+ action: 'ask',
381
+ reason: 'the request did not ask for files to be created, so this write is not the user’s stated intent',
382
+ };
383
+ }
384
+ if (request.exists) {
385
+ return {
386
+ action: 'ask',
387
+ reason: `'${request.path}' already exists — replacing existing content cannot be undone by re-running, so it is the user’s call`,
388
+ };
389
+ }
390
+ return {
391
+ action: 'proceed',
392
+ reason: 'the request asked for this file and nothing exists at that path yet — creating it destroys nothing and is undone by deleting it, so a round trip would be the manual cadence',
393
+ };
394
+ }
395
+ /**
396
+ * Decide whether a state-changing action proceeds or asks the user.
397
+ *
398
+ * Order matters, and the two never-autonomous classes are settled first so no
399
+ * later rule can turn them into an autonomous action:
400
+ *
401
+ * 1. DESTRUCTIVE -> ask. Permanent, so it stays the user's call even when
402
+ * they named it: one round trip is cheap, being wrong is not.
403
+ * 2. EXTERNAL -> ask. Someone else sees it, or it is billed.
404
+ * 3. NEITHER AUTHORIZED NOR NAMED -> ask. This is the original strictness
405
+ * (and the no-loop-context default) untouched.
406
+ * 4. NAMED BY THE REQUEST -> proceed. The user's own words are the
407
+ * authorization; re-asking is a round trip with no new information.
408
+ * 5. AUTHORIZED + RECOVERABLE -> proceed. The work was ordered and the change
409
+ * cannot strand the user.
410
+ * 6. OTHERWISE -> ask. Authorized but neither named nor measurably
411
+ * recoverable is exactly the case where a surprise is possible.
412
+ */
413
+ export function decideStateChange(request) {
414
+ if (request.changeClass === 'destructive') {
415
+ return {
416
+ action: 'ask',
417
+ reason: `${request.action} removes or irrecoverably replaces content that already exists — that is permanent, so it stays the user’s call`,
418
+ };
419
+ }
420
+ if (request.changeClass === 'external') {
421
+ return {
422
+ action: 'ask',
423
+ reason: `${request.action} has an effect outside this machine (visible to others, billed, or unrecoverable) — the user owns that decision`,
424
+ };
425
+ }
426
+ const named = request.namedByRequest === true;
427
+ if (request.authorizedByRequest !== true && !named) {
428
+ return {
429
+ action: 'ask',
430
+ reason: 'the request did not ask for this work, so carrying it out would be a surprise',
431
+ };
432
+ }
433
+ if (named) {
434
+ return {
435
+ action: 'proceed',
436
+ reason: 'the request itself names this action — asking again would be a round trip with no new information',
437
+ };
438
+ }
439
+ if (request.recoverable === true) {
440
+ return {
441
+ action: 'proceed',
442
+ reason: 'the request asked for this work and the change is recoverable, so deciding it is the agent’s job',
443
+ };
444
+ }
445
+ return {
446
+ action: 'ask',
447
+ reason: `${request.action} replaces too much of what already exists to assume it — a re-run cannot recover it, so it is the user’s call`,
448
+ };
449
+ }
450
+ // ── run_cli: which gated intents may be decided autonomously ───────────────
451
+ /**
452
+ * Confirmation-gated CLI intents that cannot be undone by re-running the
453
+ * agent: irreversible data loss, or an effect outside this machine.
454
+ *
455
+ * These keep their gate even when the request names them. One round trip is a
456
+ * small price for a decision that cannot be taken back.
457
+ */
458
+ export const IRREVERSIBLE_CLI_INTENTS = new Set([
459
+ 'history.clear',
460
+ 'memory.prune',
461
+ 'stats.cost.clear',
462
+ 'publish',
463
+ ]);
464
+ /**
465
+ * Confirmation-gated CLI intents that ARE recoverable: a service that can be
466
+ * started again, config that can be re-added, a cache that rebuilds, a skill
467
+ * that reinstalls.
468
+ *
469
+ * These proceed when the user's own request resolves to the exact command. The
470
+ * manifest flag exists because the ACTION is stateful, not because the user
471
+ * needs to approve what they just asked for.
472
+ */
473
+ export const RECOVERABLE_CLI_INTENTS = new Set([
474
+ 'dashboard.stop',
475
+ 'gateway.stop',
476
+ 'health.selfheal',
477
+ 'memory.optimize',
478
+ 'cache.clear',
479
+ 'skills.uninstall',
480
+ 'contacts.remove',
481
+ 'permissions.disallow',
482
+ 'platform.remove',
483
+ 'cron.remove',
484
+ ]);
485
+ /**
486
+ * Decide a confirmation-gated CLI intent.
487
+ *
488
+ * Note what this does NOT consult: {@link requestAuthorizesWrites}. That verdict
489
+ * answers "did the request ask for files to be created" — the wrong lens for a
490
+ * system ask like "stop the dashboard", which would read as unauthorized and
491
+ * defeat the whole rule. The evidence that matters here is whether the USER'S
492
+ * OWN WORDS resolve to this exact command (the caller resolves them with the
493
+ * same router, so the tool's ask and the user's ask are compared like for like).
494
+ */
495
+ export function decideCliIntentConfirmation(request) {
496
+ if (IRREVERSIBLE_CLI_INTENTS.has(request.intent)) {
497
+ return {
498
+ action: 'ask',
499
+ reason: `'${request.intent}' cannot be undone by re-running — one round trip is cheap, and being wrong is not`,
500
+ };
501
+ }
502
+ if (RECOVERABLE_CLI_INTENTS.has(request.intent)) {
503
+ return request.namedByRequest
504
+ ? {
505
+ action: 'proceed',
506
+ reason: `the user's own request resolves to '${request.intent}' — asking them to confirm what they just asked for is the manual cadence`,
507
+ }
508
+ : {
509
+ action: 'ask',
510
+ reason: `'${request.intent}' was not what the user asked for — it is the agent's own initiative, so it is their call`,
511
+ };
512
+ }
513
+ return {
514
+ action: 'ask',
515
+ reason: `'${request.intent}' is confirmation-gated and not classified as recoverable — asking`,
516
+ };
517
+ }
518
+ /**
519
+ * The line shown when the agent decides something on the user's behalf.
520
+ *
521
+ * Reported, never silent: the user must be able to see and reverse a judgment
522
+ * call. A decision that is invisible is indistinguishable from a bug.
523
+ */
524
+ export function autonomouslyDecidedLine(question, choice, reason) {
525
+ return `🤖 Decided without asking — ${question} → **${choice}** (${reason}). Say the word and I'll change it.`;
526
+ }
527
+ /**
528
+ * The line shown when the agent genuinely cannot decide for the user.
529
+ *
530
+ * Deliberately shaped for a one-word reply: the recommendation and the
531
+ * fallback are both named, so the user is confirming rather than composing.
532
+ */
533
+ export function consultLine(request, verdict) {
534
+ const options = (request.options ?? []).slice(0, 4);
535
+ const parts = [`❓ I need your call on this: ${request.question}`, `_${verdict.reason}_`];
536
+ if (options.length > 1) {
537
+ parts.push('', 'Options:', ...options.map((o, i) => `${i + 1}. ${o}`));
538
+ }
539
+ if (request.defaultChoice) {
540
+ parts.push('', `My recommendation: **${request.defaultChoice}** — say "go" and I'll take it.`);
541
+ }
542
+ return parts.join('\n');
543
+ }
544
+ //# sourceMappingURL=autonomy-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autonomy-policy.js","sourceRoot":"","sources":["../../src/learning/autonomy-policy.ts"],"names":[],"mappings":"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"}
@@ -112,6 +112,20 @@ export declare class CostTracker {
112
112
  getAllEntries(): CostEntry[];
113
113
  }
114
114
  export declare function getCostTracker(): CostTracker;
115
+ /**
116
+ * Spend and tokens recorded at or after `since` (epoch ms) — the per-batch
117
+ * economy window for unattended runs (G27).
118
+ *
119
+ * Read from the PERSISTED ledger rather than a session counter on purpose: a
120
+ * continuation batch runs through a fresh orchestrator (and after a resume, a
121
+ * fresh process), so an instance counter would report zero for every batch but
122
+ * the first. A timestamp window is the only measure that survives that.
123
+ */
124
+ export declare function costSince(since: number): {
125
+ costUsd: number;
126
+ tokens: number;
127
+ requests: number;
128
+ };
115
129
  /**
116
130
  * M2.2: record a call preferring MEASURED wire tokens when the provider
117
131
  * reported usage; otherwise fall back to the length-based estimate. Shared by
@@ -1 +1 @@
1
- {"version":3,"file":"cost-tracker.d.ts","sourceRoot":"","sources":["../../src/learning/cost-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,CAAC;IACxB,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;CACtB;AAqED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,CAiBR;AAID;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAmB;;IAMzC;;;;;;;;OAQG;IACH,UAAU,CACR,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,OAAO,GACjB,SAAS;IAuDZ;;;;;;OAMG;IACH,kBAAkB,CAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS;IAcZ;;;OAGG;IACH,mBAAmB,CACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS;IAMZ;;OAEG;IACH,UAAU,IAAI,WAAW;IA8BzB;;OAEG;IACH,aAAa,IAAI,MAAM;IAsCvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,aAAa,IAAI,SAAS,EAAE;CAI7B;AAKD,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D,IAAI,CAMN"}
1
+ {"version":3,"file":"cost-tracker.d.ts","sourceRoot":"","sources":["../../src/learning/cost-tracker.ts"],"names":[],"mappings":"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"}
@@ -280,6 +280,28 @@ export function getCostTracker() {
280
280
  }
281
281
  return trackerInstance;
282
282
  }
283
+ /**
284
+ * Spend and tokens recorded at or after `since` (epoch ms) — the per-batch
285
+ * economy window for unattended runs (G27).
286
+ *
287
+ * Read from the PERSISTED ledger rather than a session counter on purpose: a
288
+ * continuation batch runs through a fresh orchestrator (and after a resume, a
289
+ * fresh process), so an instance counter would report zero for every batch but
290
+ * the first. A timestamp window is the only measure that survives that.
291
+ */
292
+ export function costSince(since) {
293
+ let costUsd = 0;
294
+ let tokens = 0;
295
+ let requests = 0;
296
+ for (const entry of readCosts().entries) {
297
+ if (entry.timestamp < since)
298
+ continue;
299
+ costUsd += entry.costUsd;
300
+ tokens += entry.totalTokens;
301
+ requests += 1;
302
+ }
303
+ return { costUsd: Math.round(costUsd * 100000) / 100000, tokens, requests };
304
+ }
283
305
  /**
284
306
  * M2.2: record a call preferring MEASURED wire tokens when the provider
285
307
  * reported usage; otherwise fall back to the length-based estimate. Shared by