agent-nuvira 3.1.3 → 3.2.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 (146) 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 +194 -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 +27 -0
  26. package/dist/cli/chat.d.ts.map +1 -1
  27. package/dist/cli/chat.js +116 -6
  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 +105 -2
  32. package/dist/cli/execute.js.map +1 -1
  33. package/dist/cli/models.d.ts.map +1 -1
  34. package/dist/cli/models.js +10 -0
  35. package/dist/cli/models.js.map +1 -1
  36. package/dist/cli/trace.d.ts.map +1 -1
  37. package/dist/cli/trace.js +27 -0
  38. package/dist/cli/trace.js.map +1 -1
  39. package/dist/gateway/registry.d.ts +31 -0
  40. package/dist/gateway/registry.d.ts.map +1 -1
  41. package/dist/gateway/registry.js +143 -13
  42. package/dist/gateway/registry.js.map +1 -1
  43. package/dist/inference/model-entitlement.d.ts +46 -0
  44. package/dist/inference/model-entitlement.d.ts.map +1 -0
  45. package/dist/inference/model-entitlement.js +98 -0
  46. package/dist/inference/model-entitlement.js.map +1 -0
  47. package/dist/learning/autonomy-policy.d.ts +334 -0
  48. package/dist/learning/autonomy-policy.d.ts.map +1 -0
  49. package/dist/learning/autonomy-policy.js +500 -0
  50. package/dist/learning/autonomy-policy.js.map +1 -0
  51. package/dist/learning/credential-fingerprint.d.ts +58 -0
  52. package/dist/learning/credential-fingerprint.d.ts.map +1 -0
  53. package/dist/learning/credential-fingerprint.js +126 -0
  54. package/dist/learning/credential-fingerprint.js.map +1 -0
  55. package/dist/learning/deliverable-class.d.ts +130 -0
  56. package/dist/learning/deliverable-class.d.ts.map +1 -0
  57. package/dist/learning/deliverable-class.js +432 -0
  58. package/dist/learning/deliverable-class.js.map +1 -0
  59. package/dist/learning/long-form.d.ts +252 -0
  60. package/dist/learning/long-form.d.ts.map +1 -0
  61. package/dist/learning/long-form.js +510 -0
  62. package/dist/learning/long-form.js.map +1 -0
  63. package/dist/learning/model-first-router.d.ts +17 -0
  64. package/dist/learning/model-first-router.d.ts.map +1 -1
  65. package/dist/learning/model-first-router.js +27 -0
  66. package/dist/learning/model-first-router.js.map +1 -1
  67. package/dist/learning/model-registry.d.ts +66 -4
  68. package/dist/learning/model-registry.d.ts.map +1 -1
  69. package/dist/learning/model-registry.js +67 -6
  70. package/dist/learning/model-registry.js.map +1 -1
  71. package/dist/learning/model-warmup.d.ts +97 -2
  72. package/dist/learning/model-warmup.d.ts.map +1 -1
  73. package/dist/learning/model-warmup.js +165 -60
  74. package/dist/learning/model-warmup.js.map +1 -1
  75. package/dist/learning/prompt-layers.d.ts +61 -0
  76. package/dist/learning/prompt-layers.d.ts.map +1 -0
  77. package/dist/learning/prompt-layers.js +140 -0
  78. package/dist/learning/prompt-layers.js.map +1 -0
  79. package/dist/learning/provider-limits.d.ts +66 -0
  80. package/dist/learning/provider-limits.d.ts.map +1 -0
  81. package/dist/learning/provider-limits.js +184 -0
  82. package/dist/learning/provider-limits.js.map +1 -0
  83. package/dist/learning/reasoning-trace.d.ts +36 -1
  84. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  85. package/dist/learning/reasoning-trace.js +36 -2
  86. package/dist/learning/reasoning-trace.js.map +1 -1
  87. package/dist/learning/resilient-call.d.ts +36 -1
  88. package/dist/learning/resilient-call.d.ts.map +1 -1
  89. package/dist/learning/resilient-call.js +80 -5
  90. package/dist/learning/resilient-call.js.map +1 -1
  91. package/dist/learning/unattended-job.d.ts +293 -0
  92. package/dist/learning/unattended-job.d.ts.map +1 -0
  93. package/dist/learning/unattended-job.js +544 -0
  94. package/dist/learning/unattended-job.js.map +1 -0
  95. package/dist/learning/unattended-progress.d.ts +95 -0
  96. package/dist/learning/unattended-progress.d.ts.map +1 -0
  97. package/dist/learning/unattended-progress.js +147 -0
  98. package/dist/learning/unattended-progress.js.map +1 -0
  99. package/dist/learning/working-state.d.ts +109 -0
  100. package/dist/learning/working-state.d.ts.map +1 -0
  101. package/dist/learning/working-state.js +244 -0
  102. package/dist/learning/working-state.js.map +1 -0
  103. package/dist/nlu/conversation-gate.d.ts +24 -0
  104. package/dist/nlu/conversation-gate.d.ts.map +1 -1
  105. package/dist/nlu/conversation-gate.js +47 -3
  106. package/dist/nlu/conversation-gate.js.map +1 -1
  107. package/dist/tools/coding-tools.d.ts.map +1 -1
  108. package/dist/tools/coding-tools.js +95 -19
  109. package/dist/tools/coding-tools.js.map +1 -1
  110. package/dist/tools/edit-verification.d.ts +125 -0
  111. package/dist/tools/edit-verification.d.ts.map +1 -0
  112. package/dist/tools/edit-verification.js +237 -0
  113. package/dist/tools/edit-verification.js.map +1 -0
  114. package/dist/tools/git-tool.d.ts.map +1 -1
  115. package/dist/tools/git-tool.js +37 -4
  116. package/dist/tools/git-tool.js.map +1 -1
  117. package/dist/tools/registry.d.ts +30 -2
  118. package/dist/tools/registry.d.ts.map +1 -1
  119. package/dist/tools/registry.js +40 -11
  120. package/dist/tools/registry.js.map +1 -1
  121. package/dist/tools/run-cli.d.ts.map +1 -1
  122. package/dist/tools/run-cli.js +40 -13
  123. package/dist/tools/run-cli.js.map +1 -1
  124. package/dist/tools/run-terminal.d.ts +6 -1
  125. package/dist/tools/run-terminal.d.ts.map +1 -1
  126. package/dist/tools/run-terminal.js +158 -18
  127. package/dist/tools/run-terminal.js.map +1 -1
  128. package/dist/tools/tool-loop.d.ts +35 -0
  129. package/dist/tools/tool-loop.d.ts.map +1 -1
  130. package/dist/tools/tool-loop.js +141 -1
  131. package/dist/tools/tool-loop.js.map +1 -1
  132. package/dist/web-dashboard/chat-console.d.ts +6 -0
  133. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  134. package/dist/web-dashboard/chat-console.js.map +1 -1
  135. package/dist/web-dashboard/chat-retry.d.ts.map +1 -1
  136. package/dist/web-dashboard/chat-retry.js +10 -2
  137. package/dist/web-dashboard/chat-retry.js.map +1 -1
  138. package/dist/web-dashboard/server.d.ts.map +1 -1
  139. package/dist/web-dashboard/server.js +80 -8
  140. package/dist/web-dashboard/server.js.map +1 -1
  141. package/dist/web-dashboard/src/types.d.ts +81 -4
  142. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  143. package/package.json +1 -1
  144. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js → index-kCUkORm7.js} +2 -2
  145. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js.map → index-kCUkORm7.js.map} +1 -1
  146. package/src/web-dashboard/public/index.html +1 -1
@@ -0,0 +1,500 @@
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
+ if (PATH_RE.test(text) && CREATE_VERB_RE.test(text) && !analyzing) {
170
+ return { authorized: true, reason: 'the request names a destination path for the work' };
171
+ }
172
+ if (analyzing) {
173
+ return { authorized: false, reason: 'the request asks ABOUT the work rather than for it to be produced' };
174
+ }
175
+ if (CREATE_VERB_RE.test(text) && DELIVERABLE_NOUN_RE.test(text)) {
176
+ return { authorized: true, reason: 'the request asks for a file-shaped deliverable' };
177
+ }
178
+ // The noun list names ARTIFACT TYPES (file, app, story, script…), so it misses
179
+ // the most common dev ask there is: "fix the calculator" names no artifact at
180
+ // all, and this verdict came back NOT authorized — which meant the edit gate
181
+ // would still stop to ask for permission to fix the thing the user asked to
182
+ // have fixed. A directive verb is its own evidence: it asks for a change to
183
+ // work that already exists.
184
+ if (MAINTENANCE_VERB_RE.test(text)) {
185
+ return { authorized: true, reason: 'the request directs a change to work that already exists' };
186
+ }
187
+ return { authorized: false, reason: 'the request does not ask for files to be created' };
188
+ }
189
+ /**
190
+ * A request that asks for the work to be recorded in git history.
191
+ *
192
+ * `git commit` was gated behind `confirm:true` "after the user approved via
193
+ * ask_user" — but when the user's ask IS "commit these changes", they have
194
+ * already approved and the gate is a round trip in the most common dev flow.
195
+ * Like every other rule here, the evidence has to come from the request text.
196
+ *
197
+ * A NEGATED commit ("don't commit yet") is explicitly not a request for one,
198
+ * and an analysis opener ("how do I commit?") is asking ABOUT it.
199
+ */
200
+ export function requestRequestsCommit(request) {
201
+ const text = (request || '').trim();
202
+ if (!text)
203
+ return false;
204
+ if (ANALYSIS_OPENER_RE.test(text))
205
+ return false;
206
+ if (/\b(?:don'?t|do\s+not|never|no|without)\s+(?:\w+\s+){0,2}commit\b/i.test(text))
207
+ return false;
208
+ return /\b(?:commit|check[\s-]?in)\b/i.test(text);
209
+ }
210
+ /** Escape a literal for use inside a RegExp. */
211
+ function escapeRegexLiteral(text) {
212
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
213
+ }
214
+ /**
215
+ * Does the request name THIS file?
216
+ *
217
+ * The evidence an edit gate needs: "fix calc.ts" names the file, "update the
218
+ * README" names it. Compared on the BASENAME too, because a request says
219
+ * `calc.ts` for `src/lib/calc.ts` — that is how people actually write, and
220
+ * requiring the full path would make the rule dead on arrival.
221
+ *
222
+ * Deliberately literal (the basename or the path, as a whole word): a fuzzy
223
+ * match would let "write a story" claim `story.md` and turn a create into an
224
+ * unreviewed overwrite.
225
+ */
226
+ export function requestNamesPath(request, path) {
227
+ const text = (request || '').trim();
228
+ if (!text)
229
+ return false;
230
+ const base = path.split(/[\\/]/).pop() ?? '';
231
+ const candidates = [base, path].filter((c) => c.length >= 3);
232
+ return candidates.some((c) => new RegExp(`(?:^|[^\\w.\\/-])${escapeRegexLiteral(c)}(?:$|[^\\w.\\/-])`, 'i').test(text));
233
+ }
234
+ /**
235
+ * Above this share of a file being rewritten, an edit is a REPLACEMENT rather
236
+ * than a surgical change — and replacing most of what exists is the user's call,
237
+ * for the same reason write_file's overwrite case is.
238
+ */
239
+ export const EDIT_SURGICAL_MAX_FRACTION = 0.5;
240
+ /**
241
+ * Is this edit surgical — does it preserve most of the file it touches?
242
+ *
243
+ * The measurable line between "a fix inside a file" and "a rewrite of the
244
+ * file". A surgical edit is the iteration the verify loop is built around
245
+ * (run → read the failure → edit → re-run); a rewrite is a judgment call.
246
+ */
247
+ export function isSurgicalEdit(fileChars, oldChars, newChars) {
248
+ if (!Number.isFinite(fileChars) || fileChars <= 0)
249
+ return false;
250
+ const touched = Math.max(oldChars, newChars);
251
+ return touched / fileChars <= EDIT_SURGICAL_MAX_FRACTION;
252
+ }
253
+ // ── Permission-seeking detection ────────────────────────────────────────────
254
+ /** Direct permission requests: "do you want me to …", "shall I …". */
255
+ 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;
256
+ /** Awaiting-a-human phrasings: "awaiting your confirmation", "let me know if…". */
257
+ 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;
258
+ /**
259
+ * A question that PROPOSES doing the work rather than asking about it —
260
+ * "Create the Mahagatha interactive-book project with 20 chapter pages…?"
261
+ * (the exact shape that ended a live unattended turn).
262
+ */
263
+ const PROPOSAL_QUESTION_RE = /\?\s*$/;
264
+ /** Sentence split that keeps the shape of the original text for callers that
265
+ * need to CUT rather than merely match. */
266
+ function splitSentences(text) {
267
+ return text
268
+ .split(/(?<=[.!?。!?])\s+|\n+/)
269
+ .map((s) => s.trim())
270
+ .filter(Boolean);
271
+ }
272
+ /** Is this ONE sentence a request for permission / a proposal to do the work? */
273
+ export function isPermissionSeekingSentence(sentence) {
274
+ const s = (sentence || '').trim();
275
+ if (!s)
276
+ return false;
277
+ if (PERMISSION_ASK_RE.test(s) || PERMISSION_AWAIT_RE.test(s))
278
+ return true;
279
+ // A question that proposes CREATE_VERB + DELIVERABLE is asking WHETHER to do
280
+ // the work, not asking about it (the "Create the Mahagatha project…?" shape).
281
+ return PROPOSAL_QUESTION_RE.test(s) && CREATE_VERB_RE.test(s) && DELIVERABLE_NOUN_RE.test(s);
282
+ }
283
+ /**
284
+ * True when the answer CLOSES by asking permission to do work, rather than
285
+ * delivering it.
286
+ *
287
+ * The distinction that matters: "should I proceed?" is a stall, while "which
288
+ * title do you prefer?" is a genuine question. This detector only claims the
289
+ * permission-seeking half — callers AND it with an authorization verdict, so a
290
+ * question about work that was never authorized is untouched.
291
+ *
292
+ * Only the closing sentences are judged: a question mid-answer followed by the
293
+ * actual deliverable is narration, not a stall.
294
+ */
295
+ export function detectPermissionSeeking(content) {
296
+ const text = (content || '').trim();
297
+ if (!text)
298
+ return false;
299
+ return splitSentences(text).slice(-2).some(isPermissionSeekingSentence);
300
+ }
301
+ /**
302
+ * Remove the trailing permission-seeking sentences from an answer, keeping
303
+ * everything before them VERBATIM (deliverables are prose — re-joining split
304
+ * sentences would reformat the work itself).
305
+ *
306
+ * Used when a turn is nudged to proceed: the real work in that step must stay
307
+ * a candidate answer, but the question around it must not be what the user is
308
+ * handed — otherwise a longer "…Do you want me to…?" outranks a shorter, real
309
+ * follow-up answer under the loop's longest-substantive rule.
310
+ */
311
+ export function stripTrailingPermissionSeek(content) {
312
+ let out = (content || '').trim();
313
+ // Two sentences is the detection window — never cut deeper than it can see.
314
+ for (let i = 0; i < 2; i += 1) {
315
+ const sentences = splitSentences(out);
316
+ const last = sentences[sentences.length - 1];
317
+ if (!last || !isPermissionSeekingSentence(last))
318
+ break;
319
+ const at = out.lastIndexOf(last);
320
+ if (at <= 0)
321
+ return '';
322
+ out = out.slice(0, at).trim();
323
+ }
324
+ return out;
325
+ }
326
+ /**
327
+ * Actions that cannot be undone by re-running the agent. A question that names
328
+ * one of these is NEVER treated as reflexive permission-seeking, because the
329
+ * user genuinely has to own it.
330
+ */
331
+ 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;
332
+ /** Decide whether a state-changing write proceeds or asks the user. */
333
+ export function decideWriteConfirmation(request) {
334
+ if (!request.authorizedByRequest) {
335
+ return {
336
+ action: 'ask',
337
+ reason: 'the request did not ask for files to be created, so this write is not the user’s stated intent',
338
+ };
339
+ }
340
+ if (request.exists) {
341
+ return {
342
+ action: 'ask',
343
+ reason: `'${request.path}' already exists — replacing existing content cannot be undone by re-running, so it is the user’s call`,
344
+ };
345
+ }
346
+ return {
347
+ action: 'proceed',
348
+ 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',
349
+ };
350
+ }
351
+ /**
352
+ * Decide whether a state-changing action proceeds or asks the user.
353
+ *
354
+ * Order matters, and the two never-autonomous classes are settled first so no
355
+ * later rule can turn them into an autonomous action:
356
+ *
357
+ * 1. DESTRUCTIVE -> ask. Permanent, so it stays the user's call even when
358
+ * they named it: one round trip is cheap, being wrong is not.
359
+ * 2. EXTERNAL -> ask. Someone else sees it, or it is billed.
360
+ * 3. NEITHER AUTHORIZED NOR NAMED -> ask. This is the original strictness
361
+ * (and the no-loop-context default) untouched.
362
+ * 4. NAMED BY THE REQUEST -> proceed. The user's own words are the
363
+ * authorization; re-asking is a round trip with no new information.
364
+ * 5. AUTHORIZED + RECOVERABLE -> proceed. The work was ordered and the change
365
+ * cannot strand the user.
366
+ * 6. OTHERWISE -> ask. Authorized but neither named nor measurably
367
+ * recoverable is exactly the case where a surprise is possible.
368
+ */
369
+ export function decideStateChange(request) {
370
+ if (request.changeClass === 'destructive') {
371
+ return {
372
+ action: 'ask',
373
+ reason: `${request.action} removes or irrecoverably replaces content that already exists — that is permanent, so it stays the user’s call`,
374
+ };
375
+ }
376
+ if (request.changeClass === 'external') {
377
+ return {
378
+ action: 'ask',
379
+ reason: `${request.action} has an effect outside this machine (visible to others, billed, or unrecoverable) — the user owns that decision`,
380
+ };
381
+ }
382
+ const named = request.namedByRequest === true;
383
+ if (request.authorizedByRequest !== true && !named) {
384
+ return {
385
+ action: 'ask',
386
+ reason: 'the request did not ask for this work, so carrying it out would be a surprise',
387
+ };
388
+ }
389
+ if (named) {
390
+ return {
391
+ action: 'proceed',
392
+ reason: 'the request itself names this action — asking again would be a round trip with no new information',
393
+ };
394
+ }
395
+ if (request.recoverable === true) {
396
+ return {
397
+ action: 'proceed',
398
+ reason: 'the request asked for this work and the change is recoverable, so deciding it is the agent’s job',
399
+ };
400
+ }
401
+ return {
402
+ action: 'ask',
403
+ 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`,
404
+ };
405
+ }
406
+ // ── run_cli: which gated intents may be decided autonomously ───────────────
407
+ /**
408
+ * Confirmation-gated CLI intents that cannot be undone by re-running the
409
+ * agent: irreversible data loss, or an effect outside this machine.
410
+ *
411
+ * These keep their gate even when the request names them. One round trip is a
412
+ * small price for a decision that cannot be taken back.
413
+ */
414
+ export const IRREVERSIBLE_CLI_INTENTS = new Set([
415
+ 'history.clear',
416
+ 'memory.prune',
417
+ 'stats.cost.clear',
418
+ 'publish',
419
+ ]);
420
+ /**
421
+ * Confirmation-gated CLI intents that ARE recoverable: a service that can be
422
+ * started again, config that can be re-added, a cache that rebuilds, a skill
423
+ * that reinstalls.
424
+ *
425
+ * These proceed when the user's own request resolves to the exact command. The
426
+ * manifest flag exists because the ACTION is stateful, not because the user
427
+ * needs to approve what they just asked for.
428
+ */
429
+ export const RECOVERABLE_CLI_INTENTS = new Set([
430
+ 'dashboard.stop',
431
+ 'gateway.stop',
432
+ 'health.selfheal',
433
+ 'memory.optimize',
434
+ 'cache.clear',
435
+ 'skills.uninstall',
436
+ 'contacts.remove',
437
+ 'permissions.disallow',
438
+ 'platform.remove',
439
+ 'cron.remove',
440
+ ]);
441
+ /**
442
+ * Decide a confirmation-gated CLI intent.
443
+ *
444
+ * Note what this does NOT consult: {@link requestAuthorizesWrites}. That verdict
445
+ * answers "did the request ask for files to be created" — the wrong lens for a
446
+ * system ask like "stop the dashboard", which would read as unauthorized and
447
+ * defeat the whole rule. The evidence that matters here is whether the USER'S
448
+ * OWN WORDS resolve to this exact command (the caller resolves them with the
449
+ * same router, so the tool's ask and the user's ask are compared like for like).
450
+ */
451
+ export function decideCliIntentConfirmation(request) {
452
+ if (IRREVERSIBLE_CLI_INTENTS.has(request.intent)) {
453
+ return {
454
+ action: 'ask',
455
+ reason: `'${request.intent}' cannot be undone by re-running — one round trip is cheap, and being wrong is not`,
456
+ };
457
+ }
458
+ if (RECOVERABLE_CLI_INTENTS.has(request.intent)) {
459
+ return request.namedByRequest
460
+ ? {
461
+ action: 'proceed',
462
+ reason: `the user's own request resolves to '${request.intent}' — asking them to confirm what they just asked for is the manual cadence`,
463
+ }
464
+ : {
465
+ action: 'ask',
466
+ reason: `'${request.intent}' was not what the user asked for — it is the agent's own initiative, so it is their call`,
467
+ };
468
+ }
469
+ return {
470
+ action: 'ask',
471
+ reason: `'${request.intent}' is confirmation-gated and not classified as recoverable — asking`,
472
+ };
473
+ }
474
+ /**
475
+ * The line shown when the agent decides something on the user's behalf.
476
+ *
477
+ * Reported, never silent: the user must be able to see and reverse a judgment
478
+ * call. A decision that is invisible is indistinguishable from a bug.
479
+ */
480
+ export function autonomouslyDecidedLine(question, choice, reason) {
481
+ return `🤖 Decided without asking — ${question} → **${choice}** (${reason}). Say the word and I'll change it.`;
482
+ }
483
+ /**
484
+ * The line shown when the agent genuinely cannot decide for the user.
485
+ *
486
+ * Deliberately shaped for a one-word reply: the recommendation and the
487
+ * fallback are both named, so the user is confirming rather than composing.
488
+ */
489
+ export function consultLine(request, verdict) {
490
+ const options = (request.options ?? []).slice(0, 4);
491
+ const parts = [`❓ I need your call on this: ${request.question}`, `_${verdict.reason}_`];
492
+ if (options.length > 1) {
493
+ parts.push('', 'Options:', ...options.map((o, i) => `${i + 1}. ${o}`));
494
+ }
495
+ if (request.defaultChoice) {
496
+ parts.push('', `My recommendation: **${request.defaultChoice}** — say "go" and I'll take it.`);
497
+ }
498
+ return parts.join('\n');
499
+ }
500
+ //# 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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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Credential Fingerprint — notice when the user's keys change, and re-probe.
3
+ *
4
+ * WHY THIS EXISTS. The routing pool can only contain models a probe has
5
+ * VERIFIED, and a probe only ever ran on a cold start or a maintenance command.
6
+ * So a purchase made mid-session — the exact case where a user expects the new
7
+ * models to appear — changed nothing: the credential was fine, the catalog was
8
+ * stale, and the next run would have picked the models up only if it happened to
9
+ * cold-start. There is no per-model entitlement API to ask ("may I buy
10
+ * `meta-llama/...`?" is not a question OpenRouter answers); the only honest
11
+ * signal is *"we sent one token with this key and it answered"*. But it is
12
+ * genuinely cheap to notice that the KEY SET changed and force that probe
13
+ * instead of waiting for an accident.
14
+ *
15
+ * WHAT IS STORED: a SHA-256 digest of the credential SHAPE — which providers are
16
+ * configured, a digest of each key, each base URL, each env-var presence. Never
17
+ * a key, not even truncated, and the digest is one-way, so the file is safe to
18
+ * read, copy or commit by accident. Its only job is equality.
19
+ */
20
+ import type { ConfigManager } from '../config/manager.js';
21
+ /** Sidecar file (under the memory dir) holding the last-seen credential shape. */
22
+ export declare const CREDENTIAL_FINGERPRINT_FILENAME = "credential-fingerprint.json";
23
+ /** Path of the fingerprint sidecar (exported so tests can redirect it). */
24
+ export declare function credentialFingerprintPath(): string;
25
+ /**
26
+ * The credential SHAPE, as a list of comparable lines.
27
+ *
28
+ * Exported (and separate from the hashing) so a test — or a support call — can
29
+ * see exactly which inputs are considered without needing to reverse a digest.
30
+ * The lines carry digests, never keys.
31
+ */
32
+ export declare function credentialShapeInputs(configManager: ConfigManager): string[];
33
+ /** SHA-256 digest of the credential shape. Safe to log. */
34
+ export declare function credentialFingerprint(configManager: ConfigManager): string;
35
+ export interface CredentialChange {
36
+ /** True when the shape differs from the last one recorded. */
37
+ changed: boolean;
38
+ /** True when nothing had been recorded yet (no comparison was possible). */
39
+ firstRun: boolean;
40
+ /** The shape as of this call — already recorded by the time this returns. */
41
+ fingerprint: string;
42
+ /** The previously recorded shape, when there was one. */
43
+ previous?: string;
44
+ }
45
+ /**
46
+ * Compare the current credential shape against the last one recorded and RECORD
47
+ * the new one.
48
+ *
49
+ * Recording on every call is what makes this fire once per change rather than
50
+ * once per cycle: the caller sees `changed: true` a single time and can force a
51
+ * probe, and every later call in the same process is a no-op. `firstRun` is
52
+ * reported separately because "no previous record" is not evidence of a change —
53
+ * a fresh machine must not be treated as though a purchase just landed.
54
+ */
55
+ export declare function detectCredentialChange(configManager: ConfigManager, options?: {
56
+ path?: string;
57
+ }): CredentialChange;
58
+ //# sourceMappingURL=credential-fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-fingerprint.d.ts","sourceRoot":"","sources":["../../src/learning/credential-fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,kFAAkF;AAClF,eAAO,MAAM,+BAA+B,gCAAgC,CAAC;AAQ7E,2EAA2E;AAC3E,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAOD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM,EAAE,CAmC5E;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM,CAE1E;AAED,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9B,gBAAgB,CA4BlB"}