inferoa 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +154 -0
  3. package/dist/src/app.d.ts +16 -0
  4. package/dist/src/app.js +17 -0
  5. package/dist/src/app.js.map +1 -0
  6. package/dist/src/autoresearch/state.d.ts +106 -0
  7. package/dist/src/autoresearch/state.js +469 -0
  8. package/dist/src/autoresearch/state.js.map +1 -0
  9. package/dist/src/cli.d.ts +2 -0
  10. package/dist/src/cli.js +415 -0
  11. package/dist/src/cli.js.map +1 -0
  12. package/dist/src/code-intelligence/codegraph-engine.d.ts +55 -0
  13. package/dist/src/code-intelligence/codegraph-engine.js +593 -0
  14. package/dist/src/code-intelligence/codegraph-engine.js.map +1 -0
  15. package/dist/src/code-intelligence/hub.d.ts +37 -0
  16. package/dist/src/code-intelligence/hub.js +65 -0
  17. package/dist/src/code-intelligence/hub.js.map +1 -0
  18. package/dist/src/config/config.d.ts +12 -0
  19. package/dist/src/config/config.js +229 -0
  20. package/dist/src/config/config.js.map +1 -0
  21. package/dist/src/config/defaults.d.ts +2 -0
  22. package/dist/src/config/defaults.js +44 -0
  23. package/dist/src/config/defaults.js.map +1 -0
  24. package/dist/src/config/secret-vault.d.ts +3 -0
  25. package/dist/src/config/secret-vault.js +106 -0
  26. package/dist/src/config/secret-vault.js.map +1 -0
  27. package/dist/src/context/compressor.d.ts +33 -0
  28. package/dist/src/context/compressor.js +501 -0
  29. package/dist/src/context/compressor.js.map +1 -0
  30. package/dist/src/context/prompt.d.ts +26 -0
  31. package/dist/src/context/prompt.js +572 -0
  32. package/dist/src/context/prompt.js.map +1 -0
  33. package/dist/src/daemon/serve.d.ts +2 -0
  34. package/dist/src/daemon/serve.js +11 -0
  35. package/dist/src/daemon/serve.js.map +1 -0
  36. package/dist/src/daemon/supervisor.d.ts +33 -0
  37. package/dist/src/daemon/supervisor.js +252 -0
  38. package/dist/src/daemon/supervisor.js.map +1 -0
  39. package/dist/src/goals/state.d.ts +105 -0
  40. package/dist/src/goals/state.js +736 -0
  41. package/dist/src/goals/state.js.map +1 -0
  42. package/dist/src/model/endpoint-signals.d.ts +15 -0
  43. package/dist/src/model/endpoint-signals.js +186 -0
  44. package/dist/src/model/endpoint-signals.js.map +1 -0
  45. package/dist/src/model/gateway.d.ts +11 -0
  46. package/dist/src/model/gateway.js +455 -0
  47. package/dist/src/model/gateway.js.map +1 -0
  48. package/dist/src/plans/state.d.ts +28 -0
  49. package/dist/src/plans/state.js +123 -0
  50. package/dist/src/plans/state.js.map +1 -0
  51. package/dist/src/runtime.d.ts +92 -0
  52. package/dist/src/runtime.js +757 -0
  53. package/dist/src/runtime.js.map +1 -0
  54. package/dist/src/session/store.d.ts +84 -0
  55. package/dist/src/session/store.js +593 -0
  56. package/dist/src/session/store.js.map +1 -0
  57. package/dist/src/session/workspace.d.ts +2 -0
  58. package/dist/src/session/workspace.js +14 -0
  59. package/dist/src/session/workspace.js.map +1 -0
  60. package/dist/src/skills/registry.d.ts +24 -0
  61. package/dist/src/skills/registry.js +203 -0
  62. package/dist/src/skills/registry.js.map +1 -0
  63. package/dist/src/tools/autoresearch-tools.d.ts +6 -0
  64. package/dist/src/tools/autoresearch-tools.js +412 -0
  65. package/dist/src/tools/autoresearch-tools.js.map +1 -0
  66. package/dist/src/tools/clarify-tool.d.ts +3 -0
  67. package/dist/src/tools/clarify-tool.js +107 -0
  68. package/dist/src/tools/clarify-tool.js.map +1 -0
  69. package/dist/src/tools/code-intelligence.d.ts +15 -0
  70. package/dist/src/tools/code-intelligence.js +391 -0
  71. package/dist/src/tools/code-intelligence.js.map +1 -0
  72. package/dist/src/tools/context.d.ts +11 -0
  73. package/dist/src/tools/context.js +2 -0
  74. package/dist/src/tools/context.js.map +1 -0
  75. package/dist/src/tools/goal-tools.d.ts +3 -0
  76. package/dist/src/tools/goal-tools.js +279 -0
  77. package/dist/src/tools/goal-tools.js.map +1 -0
  78. package/dist/src/tools/omni-tools.d.ts +8 -0
  79. package/dist/src/tools/omni-tools.js +349 -0
  80. package/dist/src/tools/omni-tools.js.map +1 -0
  81. package/dist/src/tools/permissions.d.ts +11 -0
  82. package/dist/src/tools/permissions.js +74 -0
  83. package/dist/src/tools/permissions.js.map +1 -0
  84. package/dist/src/tools/plan-tools.d.ts +3 -0
  85. package/dist/src/tools/plan-tools.js +314 -0
  86. package/dist/src/tools/plan-tools.js.map +1 -0
  87. package/dist/src/tools/process-tools.d.ts +6 -0
  88. package/dist/src/tools/process-tools.js +199 -0
  89. package/dist/src/tools/process-tools.js.map +1 -0
  90. package/dist/src/tools/registry.d.ts +20 -0
  91. package/dist/src/tools/registry.js +187 -0
  92. package/dist/src/tools/registry.js.map +1 -0
  93. package/dist/src/tools/schemas.d.ts +3 -0
  94. package/dist/src/tools/schemas.js +500 -0
  95. package/dist/src/tools/schemas.js.map +1 -0
  96. package/dist/src/tools/skill-tools.d.ts +6 -0
  97. package/dist/src/tools/skill-tools.js +124 -0
  98. package/dist/src/tools/skill-tools.js.map +1 -0
  99. package/dist/src/tools/text-args.d.ts +5 -0
  100. package/dist/src/tools/text-args.js +22 -0
  101. package/dist/src/tools/text-args.js.map +1 -0
  102. package/dist/src/tools/web-search.d.ts +5 -0
  103. package/dist/src/tools/web-search.js +602 -0
  104. package/dist/src/tools/web-search.js.map +1 -0
  105. package/dist/src/tools/workspace-tools.d.ts +17 -0
  106. package/dist/src/tools/workspace-tools.js +561 -0
  107. package/dist/src/tools/workspace-tools.js.map +1 -0
  108. package/dist/src/tui/activity.d.ts +11 -0
  109. package/dist/src/tui/activity.js +75 -0
  110. package/dist/src/tui/activity.js.map +1 -0
  111. package/dist/src/tui/ansi.d.ts +24 -0
  112. package/dist/src/tui/ansi.js +131 -0
  113. package/dist/src/tui/ansi.js.map +1 -0
  114. package/dist/src/tui/app.d.ts +163 -0
  115. package/dist/src/tui/app.js +4204 -0
  116. package/dist/src/tui/app.js.map +1 -0
  117. package/dist/src/tui/cache-footer.d.ts +21 -0
  118. package/dist/src/tui/cache-footer.js +75 -0
  119. package/dist/src/tui/cache-footer.js.map +1 -0
  120. package/dist/src/tui/clarify.d.ts +14 -0
  121. package/dist/src/tui/clarify.js +187 -0
  122. package/dist/src/tui/clarify.js.map +1 -0
  123. package/dist/src/tui/composer.d.ts +79 -0
  124. package/dist/src/tui/composer.js +592 -0
  125. package/dist/src/tui/composer.js.map +1 -0
  126. package/dist/src/tui/event-view.d.ts +5 -0
  127. package/dist/src/tui/event-view.js +392 -0
  128. package/dist/src/tui/event-view.js.map +1 -0
  129. package/dist/src/tui/home.d.ts +7 -0
  130. package/dist/src/tui/home.js +92 -0
  131. package/dist/src/tui/home.js.map +1 -0
  132. package/dist/src/tui/markdown.d.ts +18 -0
  133. package/dist/src/tui/markdown.js +271 -0
  134. package/dist/src/tui/markdown.js.map +1 -0
  135. package/dist/src/tui/mode-footer.d.ts +9 -0
  136. package/dist/src/tui/mode-footer.js +62 -0
  137. package/dist/src/tui/mode-footer.js.map +1 -0
  138. package/dist/src/tui/plan-view.d.ts +8 -0
  139. package/dist/src/tui/plan-view.js +45 -0
  140. package/dist/src/tui/plan-view.js.map +1 -0
  141. package/dist/src/tui/prompt-queue.d.ts +18 -0
  142. package/dist/src/tui/prompt-queue.js +27 -0
  143. package/dist/src/tui/prompt-queue.js.map +1 -0
  144. package/dist/src/tui/resize.d.ts +7 -0
  145. package/dist/src/tui/resize.js +15 -0
  146. package/dist/src/tui/resize.js.map +1 -0
  147. package/dist/src/tui/session-picker.d.ts +10 -0
  148. package/dist/src/tui/session-picker.js +17 -0
  149. package/dist/src/tui/session-picker.js.map +1 -0
  150. package/dist/src/tui/session-transcript.d.ts +2 -0
  151. package/dist/src/tui/session-transcript.js +44 -0
  152. package/dist/src/tui/session-transcript.js.map +1 -0
  153. package/dist/src/tui/slash-notice.d.ts +2 -0
  154. package/dist/src/tui/slash-notice.js +9 -0
  155. package/dist/src/tui/slash-notice.js.map +1 -0
  156. package/dist/src/tui/slash.d.ts +21 -0
  157. package/dist/src/tui/slash.js +103 -0
  158. package/dist/src/tui/slash.js.map +1 -0
  159. package/dist/src/tui/splash.d.ts +4 -0
  160. package/dist/src/tui/splash.js +64 -0
  161. package/dist/src/tui/splash.js.map +1 -0
  162. package/dist/src/tui/tool-renderer.d.ts +6 -0
  163. package/dist/src/tui/tool-renderer.js +1024 -0
  164. package/dist/src/tui/tool-renderer.js.map +1 -0
  165. package/dist/src/tui/transcript-spacing.d.ts +1 -0
  166. package/dist/src/tui/transcript-spacing.js +4 -0
  167. package/dist/src/tui/transcript-spacing.js.map +1 -0
  168. package/dist/src/types.d.ts +220 -0
  169. package/dist/src/types.js +2 -0
  170. package/dist/src/types.js.map +1 -0
  171. package/dist/src/util/abort.d.ts +3 -0
  172. package/dist/src/util/abort.js +19 -0
  173. package/dist/src/util/abort.js.map +1 -0
  174. package/dist/src/util/clock.d.ts +2 -0
  175. package/dist/src/util/clock.js +7 -0
  176. package/dist/src/util/clock.js.map +1 -0
  177. package/dist/src/util/fs.d.ts +13 -0
  178. package/dist/src/util/fs.js +75 -0
  179. package/dist/src/util/fs.js.map +1 -0
  180. package/dist/src/util/hash.d.ts +6 -0
  181. package/dist/src/util/hash.js +50 -0
  182. package/dist/src/util/hash.js.map +1 -0
  183. package/dist/src/util/limit.d.ts +11 -0
  184. package/dist/src/util/limit.js +29 -0
  185. package/dist/src/util/limit.js.map +1 -0
  186. package/dist/src/util/types.d.ts +22 -0
  187. package/dist/src/util/types.js +33 -0
  188. package/dist/src/util/types.js.map +1 -0
  189. package/dist/src/validation/acceptance.d.ts +12 -0
  190. package/dist/src/validation/acceptance.js +251 -0
  191. package/dist/src/validation/acceptance.js.map +1 -0
  192. package/dist/src/validation/milestone.d.ts +2 -0
  193. package/dist/src/validation/milestone.js +141 -0
  194. package/dist/src/validation/milestone.js.map +1 -0
  195. package/docs/final-acceptance-task.md +193 -0
  196. package/docs/public-source-hygiene.md +21 -0
  197. package/docs/roadmap.md +265 -0
  198. package/docs/tui-product-design.md +270 -0
  199. package/package.json +67 -0
  200. package/skills/coding-workflow/SKILL.md +16 -0
@@ -0,0 +1,736 @@
1
+ import { randomId } from "../util/hash.js";
2
+ import { truncateText } from "../util/limit.js";
3
+ const PLAN_PROMPT_BODY_LIMIT = 6000;
4
+ export function readGoalState(store, sessionId) {
5
+ const event = latestGoalEvent(store.listEvents(sessionId));
6
+ if (!event) {
7
+ return undefined;
8
+ }
9
+ return parseGoalState(event.data);
10
+ }
11
+ export function createGoalState(input, now = new Date()) {
12
+ const objective = input.objective.trim();
13
+ if (!objective) {
14
+ throw new Error("objective is required");
15
+ }
16
+ validateTokenBudget(input.token_budget);
17
+ const timestamp = now.toISOString();
18
+ return {
19
+ enabled: true,
20
+ goal: {
21
+ id: randomId("goal"),
22
+ objective,
23
+ status: "active",
24
+ token_budget: input.token_budget,
25
+ tokens_used: 0,
26
+ time_used_ms: 0,
27
+ time_used_seconds: 0,
28
+ tool_rounds_used: 0,
29
+ tool_calls_used: 0,
30
+ created_at: timestamp,
31
+ updated_at: timestamp,
32
+ },
33
+ };
34
+ }
35
+ export function replaceGoalPlanning(state, input, now = new Date()) {
36
+ const next = cloneGoalState(state);
37
+ next.goal.planning = createGoalPlanning(input, now);
38
+ next.goal.updated_at = next.goal.planning.updated_at;
39
+ return next;
40
+ }
41
+ export function attachGoalPlanSnapshot(state, plan, now = new Date()) {
42
+ let next = cloneGoalState(state);
43
+ next.goal.plan = { ...plan };
44
+ const steps = goalPlanningStepsFromMarkdown(plan.body);
45
+ if (steps.length) {
46
+ next = syncApprovedPlanIntoGoalPlanning(next, steps, plan.summary, now);
47
+ }
48
+ next.goal.updated_at = now.toISOString();
49
+ return next;
50
+ }
51
+ function syncApprovedPlanIntoGoalPlanning(state, planSteps, summary, now) {
52
+ if (!state.goal.planning) {
53
+ return replaceGoalPlanning(state, {
54
+ summary,
55
+ steps: planSteps,
56
+ }, now);
57
+ }
58
+ const existing = state.goal.planning;
59
+ const byId = new Map(existing.steps.map((step) => [step.id, step]));
60
+ const byTitle = new Map(existing.steps.map((step) => [goalStepTitleKey(step.title), step]));
61
+ const used = new Set();
62
+ const mergedSteps = planSteps.map((step, index) => {
63
+ const title = step.title.trim();
64
+ const provisionalId = normalizeGoalStepId(step.id, title, index, new Set());
65
+ const prior = byId.get(provisionalId) ?? byTitle.get(goalStepTitleKey(title));
66
+ const id = normalizeGoalStepId(prior?.id ?? step.id, title, index, used);
67
+ used.add(id);
68
+ return {
69
+ id,
70
+ title,
71
+ status: prior?.status ?? step.status ?? "pending",
72
+ notes: prior?.notes ?? cleanOptionalString(step.notes),
73
+ evidence: prior?.evidence ? cloneJsonObject(prior.evidence) : step.evidence ? cloneJsonObject(step.evidence) : undefined,
74
+ };
75
+ });
76
+ return replaceGoalPlanning(state, {
77
+ summary: summary ?? existing.summary,
78
+ active_step_id: existing.active_step_id && mergedSteps.some((step) => step.id === existing.active_step_id) ? existing.active_step_id : undefined,
79
+ steps: mergedSteps,
80
+ }, now);
81
+ }
82
+ export function updateGoalPlanningStep(state, input, now = new Date()) {
83
+ if (!state.goal.planning) {
84
+ throw new Error("goal planning has not been decomposed yet");
85
+ }
86
+ const stepId = input.step_id.trim();
87
+ if (!stepId) {
88
+ throw new Error("step_id is required");
89
+ }
90
+ const timestamp = now.toISOString();
91
+ const next = cloneGoalState(state);
92
+ const planning = next.goal.planning;
93
+ let step = planning.steps.find((item) => item.id === stepId);
94
+ if (!step) {
95
+ const title = input.title?.trim();
96
+ if (!title) {
97
+ throw new Error(`unknown goal step: ${stepId}`);
98
+ }
99
+ step = {
100
+ id: normalizeGoalStepId(stepId, title, planning.steps.length, new Set(planning.steps.map((item) => item.id))),
101
+ title,
102
+ status: input.status ?? "pending",
103
+ updated_at: timestamp,
104
+ };
105
+ planning.steps.push(step);
106
+ }
107
+ if (input.title !== undefined) {
108
+ const title = input.title.trim();
109
+ if (!title) {
110
+ throw new Error("step title cannot be empty");
111
+ }
112
+ step.title = title;
113
+ }
114
+ if (input.status) {
115
+ step.status = input.status;
116
+ }
117
+ if (input.notes !== undefined) {
118
+ const notes = input.notes.trim();
119
+ if (notes) {
120
+ step.notes = notes;
121
+ }
122
+ else {
123
+ delete step.notes;
124
+ }
125
+ }
126
+ if (input.evidence !== undefined) {
127
+ step.evidence = cloneJsonObject(input.evidence);
128
+ }
129
+ step.updated_at = timestamp;
130
+ if (input.active_step_id !== undefined) {
131
+ planning.active_step_id = normalizeExistingStepId(input.active_step_id, planning.steps);
132
+ }
133
+ else if (step.status === "in_progress") {
134
+ planning.active_step_id = step.id;
135
+ }
136
+ else if (planning.active_step_id === step.id && isTerminalGoalStepStatus(step.status)) {
137
+ planning.active_step_id = firstNonTerminalStep(planning.steps)?.id;
138
+ }
139
+ const active = planning.active_step_id ? planning.steps.find((item) => item.id === planning.active_step_id) : undefined;
140
+ if (active && active.status === "pending") {
141
+ active.status = "in_progress";
142
+ active.updated_at = timestamp;
143
+ }
144
+ planning.updated_at = timestamp;
145
+ next.goal.updated_at = timestamp;
146
+ return next;
147
+ }
148
+ export function createGoalPlanning(input, now = new Date()) {
149
+ const timestamp = now.toISOString();
150
+ const used = new Set();
151
+ const steps = input.steps.map((step, index) => {
152
+ const title = step.title.trim();
153
+ if (!title) {
154
+ throw new Error("goal planning steps must have non-empty titles");
155
+ }
156
+ const id = normalizeGoalStepId(step.id, title, index, used);
157
+ used.add(id);
158
+ return {
159
+ id,
160
+ title,
161
+ status: step.status ?? "pending",
162
+ notes: cleanOptionalString(step.notes),
163
+ evidence: step.evidence ? cloneJsonObject(step.evidence) : undefined,
164
+ updated_at: timestamp,
165
+ };
166
+ });
167
+ if (!steps.length) {
168
+ throw new Error("goal planning requires at least one step");
169
+ }
170
+ const activeStepId = normalizeExistingStepId(input.active_step_id, steps) ?? firstNonTerminalStep(steps)?.id;
171
+ const activeStep = activeStepId ? steps.find((step) => step.id === activeStepId) : undefined;
172
+ if (activeStep && activeStep.status === "pending") {
173
+ activeStep.status = "in_progress";
174
+ }
175
+ return {
176
+ summary: cleanOptionalString(input.summary),
177
+ active_step_id: activeStepId,
178
+ steps,
179
+ updated_at: timestamp,
180
+ };
181
+ }
182
+ export function writeGoalState(store, sessionId, state, runId) {
183
+ const cloned = cloneGoalState(state);
184
+ store.appendEvent({
185
+ session_id: sessionId,
186
+ run_id: runId,
187
+ type: "goal.updated",
188
+ data: goalStateToJson(cloned),
189
+ });
190
+ return cloned;
191
+ }
192
+ export function goalCompletionReportForRun(store, sessionId, runId) {
193
+ return goalCompletionForRun(store, sessionId, runId)?.report;
194
+ }
195
+ export function recordGoalCompletionReport(store, sessionId, runId) {
196
+ const completion = goalCompletionForRun(store, sessionId, runId);
197
+ if (!completion) {
198
+ return undefined;
199
+ }
200
+ const state = readGoalState(store, sessionId);
201
+ if (!state) {
202
+ return undefined;
203
+ }
204
+ const exists = store.listEvents(sessionId).some((event) => event.run_id === runId && event.type === "goal.completion_report");
205
+ if (!exists) {
206
+ const data = {
207
+ goal_objective: completion.objective,
208
+ report: completion.report,
209
+ tool_rounds: state.goal.tool_rounds_used,
210
+ tool_calls: state.goal.tool_calls_used,
211
+ tokens: state.goal.tokens_used,
212
+ duration_ms: goalDurationMs(state.goal),
213
+ };
214
+ if (state.goal.summary) {
215
+ data.completion_summary = state.goal.summary;
216
+ }
217
+ store.appendEvent({
218
+ session_id: sessionId,
219
+ run_id: runId,
220
+ type: "goal.completion_report",
221
+ data,
222
+ });
223
+ }
224
+ return completion;
225
+ }
226
+ export function goalStateToJson(state) {
227
+ return {
228
+ enabled: state.enabled,
229
+ goal: state.goal,
230
+ };
231
+ }
232
+ export function applyGoalUsage(store, sessionId, usage, runId) {
233
+ const state = readGoalState(store, sessionId);
234
+ if (!state || !shouldAccountGoalUsage(store, sessionId, state, runId)) {
235
+ return state;
236
+ }
237
+ const tokens = Math.max(0, Math.trunc(usage.tokens ?? 0));
238
+ const seconds = Math.max(0, Math.trunc(usage.time_seconds ?? 0));
239
+ const durationMs = Math.max(0, Math.trunc(usage.duration_ms ?? seconds * 1000));
240
+ const toolRounds = Math.max(0, Math.trunc(usage.tool_rounds ?? 0));
241
+ const toolCalls = Math.max(0, Math.trunc(usage.tool_calls ?? 0));
242
+ if (tokens === 0 && durationMs === 0 && toolRounds === 0 && toolCalls === 0) {
243
+ return state;
244
+ }
245
+ const next = cloneGoalState(state);
246
+ next.goal.tokens_used += tokens;
247
+ next.goal.time_used_ms = goalDurationMs(next.goal) + durationMs;
248
+ next.goal.time_used_seconds = Math.floor(next.goal.time_used_ms / 1000);
249
+ next.goal.tool_rounds_used += toolRounds;
250
+ next.goal.tool_calls_used += toolCalls;
251
+ next.goal.updated_at = new Date().toISOString();
252
+ if (next.goal.token_budget !== undefined &&
253
+ next.goal.tokens_used >= next.goal.token_budget &&
254
+ next.goal.status === "active") {
255
+ next.goal.status = "budget-limited";
256
+ }
257
+ return writeGoalState(store, sessionId, next, runId);
258
+ }
259
+ export function modelUsageTokenCost(usage) {
260
+ if (!usage) {
261
+ return 0;
262
+ }
263
+ if (typeof usage.total_tokens === "number" && Number.isFinite(usage.total_tokens)) {
264
+ return Math.max(0, Math.trunc(usage.total_tokens));
265
+ }
266
+ const prompt = numeric(usage.prompt_tokens);
267
+ const completion = numeric(usage.completion_tokens);
268
+ return Math.max(0, prompt + completion);
269
+ }
270
+ export function renderGoalModeSection(state) {
271
+ if (!state?.enabled || !isActiveGoalPromptStatus(state.goal.status)) {
272
+ return undefined;
273
+ }
274
+ const goal = state.goal;
275
+ const budgetLine = goal.token_budget === undefined
276
+ ? "token budget: none"
277
+ : `token budget: ${goal.token_budget}; tokens used: ${goal.tokens_used}; remaining tokens: ${Math.max(0, goal.token_budget - goal.tokens_used)}`;
278
+ const loopLine = `tool loops used: ${goal.tool_rounds_used}; tool calls used: ${goal.tool_calls_used}`;
279
+ const statusLine = goal.status === "budget-limited"
280
+ ? "status: budget-limited; finish with a concise final answer or call goal complete when the objective is actually done."
281
+ : `status: ${goal.status}`;
282
+ return [
283
+ "A goal-mode objective is active for this session.",
284
+ renderTrustedObjective(goal.objective),
285
+ statusLine,
286
+ budgetLine,
287
+ loopLine,
288
+ `time used seconds: ${goal.time_used_seconds}`,
289
+ `time used ms: ${goalDurationMs(goal)}`,
290
+ goal.planning ? renderGoalPlanning(goal.planning) : "Internal goal plan: not decomposed yet.",
291
+ goal.plan ? renderApprovedPlan(goal.plan, Boolean(goal.planning)) : undefined,
292
+ goal.planning
293
+ ? "Keep the internal goal plan current with goal op=update_step as findings, edits, and verification change."
294
+ : "For broad or multi-step work, call goal op=decompose with concrete steps before risky edits.",
295
+ "Work on the objective until it is genuinely handled. Use the goal tool to inspect, resume, complete, or drop goal state when appropriate.",
296
+ "When completing the goal, include a completion summary in the goal tool call.",
297
+ "Do not mark the goal complete merely because the turn is ending or the budget is low.",
298
+ ]
299
+ .filter((line) => Boolean(line))
300
+ .join("\n");
301
+ }
302
+ export function completionBudgetReport(goal) {
303
+ const usage = goal.token_budget === undefined
304
+ ? `${goal.tokens_used} tokens used`
305
+ : `${goal.tokens_used} of ${goal.token_budget} tokens used`;
306
+ return `Goal achieved. ${countLabel(goal.tool_rounds_used, "loop")} · ${countLabel(goal.tool_calls_used, "tool call")} · ${formatDurationMs(goalDurationMs(goal))} · ${usage}.`;
307
+ }
308
+ export function goalDurationMs(goal) {
309
+ return Math.max(0, Math.trunc(goal.time_used_ms ?? goal.time_used_seconds * 1000));
310
+ }
311
+ export function formatGoalDuration(goal) {
312
+ return formatDurationMs(goalDurationMs(goal));
313
+ }
314
+ export function renderTrustedObjective(objective) {
315
+ return `<objective>\n${escapeXmlText(objective)}\n</objective>`;
316
+ }
317
+ export function escapeXmlText(input) {
318
+ return input.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
319
+ }
320
+ export function cloneGoalState(state) {
321
+ return {
322
+ enabled: state.enabled,
323
+ goal: {
324
+ ...state.goal,
325
+ planning: state.goal.planning ? cloneGoalPlanning(state.goal.planning) : undefined,
326
+ plan: state.goal.plan ? { ...state.goal.plan } : undefined,
327
+ },
328
+ };
329
+ }
330
+ export function incompleteGoalPlanningSteps(goal) {
331
+ return goal.planning?.steps.filter((step) => !isTerminalGoalStepStatus(step.status)) ?? [];
332
+ }
333
+ export function incompleteGoalPlanningMessage(goal) {
334
+ const incomplete = incompleteGoalPlanningSteps(goal);
335
+ if (!incomplete.length) {
336
+ return undefined;
337
+ }
338
+ const visible = incomplete.slice(0, 8).map((step) => step.id);
339
+ const suffix = incomplete.length > visible.length ? `, and ${incomplete.length - visible.length} more` : "";
340
+ return `Cannot complete goal with unfinished internal plan steps: ${visible.join(", ")}${suffix}`;
341
+ }
342
+ export function goalPlanningProgressSummary(planning) {
343
+ const counts = new Map();
344
+ for (const step of planning.steps) {
345
+ counts.set(step.status, (counts.get(step.status) ?? 0) + 1);
346
+ }
347
+ const parts = [
348
+ countPart(counts, "completed", "completed"),
349
+ countPart(counts, "in_progress", "in progress"),
350
+ countPart(counts, "blocked", "blocked"),
351
+ countPart(counts, "pending", "pending"),
352
+ countPart(counts, "skipped", "skipped"),
353
+ ].filter((part) => Boolean(part));
354
+ return parts.length ? parts.join(" · ") : "no steps";
355
+ }
356
+ export function goalPlanningStepsFromMarkdown(body) {
357
+ if (!body) {
358
+ return [];
359
+ }
360
+ return body
361
+ .split(/\r?\n/)
362
+ .map(parseGoalPlanningStepLine)
363
+ .filter((step) => Boolean(step));
364
+ }
365
+ export function validateTokenBudget(value) {
366
+ if (value === undefined) {
367
+ return;
368
+ }
369
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
370
+ throw new Error("token_budget must be a positive integer when provided");
371
+ }
372
+ }
373
+ export function isAccountingGoal(goal) {
374
+ return goal.status === "active" || goal.status === "budget-limited";
375
+ }
376
+ function shouldAccountGoalUsage(store, sessionId, state, runId) {
377
+ if (state.enabled && isAccountingGoal(state.goal)) {
378
+ return true;
379
+ }
380
+ if (!runId || (state.goal.status !== "complete" && state.goal.status !== "dropped")) {
381
+ return false;
382
+ }
383
+ return store.listEvents(sessionId).some((event) => {
384
+ if (event.run_id !== runId || event.type !== "goal.updated") {
385
+ return false;
386
+ }
387
+ const eventState = parseGoalState(event.data);
388
+ return eventState?.goal.id === state.goal.id && (eventState.goal.status === "complete" || eventState.goal.status === "dropped");
389
+ });
390
+ }
391
+ function goalCompletionForRun(store, sessionId, runId) {
392
+ const state = readGoalState(store, sessionId);
393
+ if (!state || state.goal.status !== "complete") {
394
+ return undefined;
395
+ }
396
+ const completedInRun = store.listEvents(sessionId).some((event) => {
397
+ if (event.run_id !== runId || event.type !== "goal.updated") {
398
+ return false;
399
+ }
400
+ const eventState = parseGoalState(event.data);
401
+ return eventState?.goal.id === state.goal.id && eventState.goal.status === "complete";
402
+ });
403
+ const report = completedInRun ? completionBudgetReport(state.goal) : undefined;
404
+ return report ? { objective: state.goal.objective, report } : undefined;
405
+ }
406
+ function isActiveGoalPromptStatus(status) {
407
+ return status === "active" || status === "budget-limited";
408
+ }
409
+ function latestGoalEvent(events) {
410
+ return events.filter((event) => event.type === "goal.updated").at(-1);
411
+ }
412
+ function parseGoalState(data) {
413
+ const goal = data.goal;
414
+ if (!goal || typeof goal !== "object" || Array.isArray(goal)) {
415
+ return undefined;
416
+ }
417
+ const candidate = goal;
418
+ const objective = typeof candidate.objective === "string" ? candidate.objective : "";
419
+ const status = parseGoalStatus(candidate.status);
420
+ const id = typeof candidate.id === "string" ? candidate.id : "";
421
+ if (!id || !objective || !status) {
422
+ return undefined;
423
+ }
424
+ const tokenBudget = numericOrUndefined(candidate.token_budget);
425
+ return {
426
+ enabled: data.enabled === true,
427
+ goal: {
428
+ id,
429
+ objective,
430
+ status,
431
+ token_budget: tokenBudget,
432
+ tokens_used: numeric(candidate.tokens_used),
433
+ time_used_ms: durationMsFromGoalData(candidate),
434
+ time_used_seconds: numeric(candidate.time_used_seconds),
435
+ tool_rounds_used: numeric(candidate.tool_rounds_used),
436
+ tool_calls_used: numeric(candidate.tool_calls_used),
437
+ planning: parseGoalPlanning(candidate.planning),
438
+ plan: parseGoalPlan(candidate.plan),
439
+ summary: optionalString(candidate.summary),
440
+ created_at: typeof candidate.created_at === "string" ? candidate.created_at : "",
441
+ updated_at: typeof candidate.updated_at === "string" ? candidate.updated_at : "",
442
+ },
443
+ };
444
+ }
445
+ function renderApprovedPlan(plan, hasInternalPlanning) {
446
+ const bodySyncedIntoInternalPlan = hasInternalPlanning && goalPlanningStepsFromMarkdown(plan.body).length > 0;
447
+ return [
448
+ "Approved plan:",
449
+ `<plan_objective>\n${escapeXmlText(plan.objective)}\n</plan_objective>`,
450
+ plan.summary ? `Plan summary: ${escapeXmlText(plan.summary)}` : undefined,
451
+ bodySyncedIntoInternalPlan ? "Body synced into the internal goal plan above." : plan.body ? `Plan body:\n${escapeXmlText(truncateText(plan.body, PLAN_PROMPT_BODY_LIMIT).text)}` : undefined,
452
+ ]
453
+ .filter((line) => Boolean(line))
454
+ .join("\n");
455
+ }
456
+ function renderGoalPlanning(planning) {
457
+ const active = planning.active_step_id ? planning.steps.find((step) => step.id === planning.active_step_id) : undefined;
458
+ return [
459
+ "Internal goal plan:",
460
+ planning.summary ? `Plan summary: ${escapeXmlText(planning.summary)}` : undefined,
461
+ active ? `Active step: ${escapeXmlText(active.id)} ${escapeXmlText(active.title)}` : undefined,
462
+ `Progress: ${goalPlanningProgressSummary(planning)}`,
463
+ ...planning.steps.flatMap((step) => renderGoalPlanningStep(step)),
464
+ ]
465
+ .filter((line) => Boolean(line))
466
+ .join("\n");
467
+ }
468
+ function renderGoalPlanningStep(step) {
469
+ const marker = stepMarker(step.status);
470
+ const lines = [`[${marker}] ${escapeXmlText(step.id)} ${escapeXmlText(step.title)}`];
471
+ if (step.notes) {
472
+ lines.push(`notes: ${escapeXmlText(truncateEvidenceText(step.notes, 500))}`);
473
+ }
474
+ if (step.evidence) {
475
+ lines.push(`evidence: ${escapeXmlText(compactEvidenceSummary(step.evidence))}`);
476
+ }
477
+ return lines;
478
+ }
479
+ function compactEvidenceSummary(value) {
480
+ return compactEvidenceObject(value, 0) || "recorded";
481
+ }
482
+ function compactEvidenceObject(value, depth) {
483
+ const entries = Object.keys(value)
484
+ .sort()
485
+ .slice(0, depth === 0 ? 8 : 4)
486
+ .map((key) => {
487
+ const compact = compactEvidenceValue(value[key], depth + 1);
488
+ return compact ? `${key}=${compact}` : "";
489
+ })
490
+ .filter(Boolean);
491
+ const omitted = Math.max(0, Object.keys(value).length - (depth === 0 ? 8 : 4));
492
+ if (omitted > 0) {
493
+ entries.push(`${omitted} more`);
494
+ }
495
+ return entries.join("; ");
496
+ }
497
+ function compactEvidenceValue(value, depth) {
498
+ if (value === null || value === undefined) {
499
+ return "";
500
+ }
501
+ if (typeof value === "string") {
502
+ return truncateEvidenceText(value);
503
+ }
504
+ if (typeof value === "number" || typeof value === "boolean") {
505
+ return String(value);
506
+ }
507
+ if (Array.isArray(value)) {
508
+ const items = value.map((item) => compactEvidenceValue(item, depth)).filter(Boolean).slice(0, 6);
509
+ const omitted = Math.max(0, value.length - items.length);
510
+ return `${items.join(", ")}${omitted ? `, ${omitted} more` : ""}`;
511
+ }
512
+ if (typeof value === "object") {
513
+ if (depth >= 3) {
514
+ return "object";
515
+ }
516
+ const compact = compactEvidenceObject(value, depth);
517
+ return compact ? `(${compact})` : "";
518
+ }
519
+ return truncateEvidenceText(String(value));
520
+ }
521
+ function truncateEvidenceText(value, max = 160) {
522
+ const normalized = value.replace(/\s+/g, " ").trim();
523
+ return normalized.length > max ? `${normalized.slice(0, max - 3)}...` : normalized;
524
+ }
525
+ function parseGoalPlan(value) {
526
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
527
+ return undefined;
528
+ }
529
+ const data = value;
530
+ const id = optionalString(data.id);
531
+ const objective = optionalString(data.objective);
532
+ const approvedAt = optionalString(data.approved_at);
533
+ if (!id || !objective || !approvedAt) {
534
+ return undefined;
535
+ }
536
+ return {
537
+ id,
538
+ objective,
539
+ summary: optionalString(data.summary),
540
+ body: optionalString(data.body),
541
+ approved_at: approvedAt,
542
+ };
543
+ }
544
+ function parseGoalPlanning(value) {
545
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
546
+ return undefined;
547
+ }
548
+ const data = value;
549
+ const rawSteps = Array.isArray(data.steps) ? data.steps : [];
550
+ const steps = rawSteps.map(parseGoalPlanningStep).filter((step) => Boolean(step));
551
+ if (!steps.length) {
552
+ return undefined;
553
+ }
554
+ const activeStepId = normalizeExistingStepId(optionalString(data.active_step_id), steps) ?? firstNonTerminalStep(steps)?.id;
555
+ return {
556
+ summary: optionalString(data.summary),
557
+ active_step_id: activeStepId,
558
+ steps,
559
+ updated_at: typeof data.updated_at === "string" ? data.updated_at : "",
560
+ };
561
+ }
562
+ function parseGoalPlanningStep(value) {
563
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
564
+ return undefined;
565
+ }
566
+ const data = value;
567
+ const id = optionalString(data.id);
568
+ const title = optionalString(data.title);
569
+ const status = parseGoalStepStatus(data.status);
570
+ if (!id || !title || !status) {
571
+ return undefined;
572
+ }
573
+ return {
574
+ id,
575
+ title,
576
+ status,
577
+ notes: optionalString(data.notes),
578
+ evidence: parseJsonObject(data.evidence),
579
+ updated_at: typeof data.updated_at === "string" ? data.updated_at : "",
580
+ };
581
+ }
582
+ function parseGoalStatus(value) {
583
+ return value === "active" || value === "paused" || value === "budget-limited" || value === "complete" || value === "dropped"
584
+ ? value
585
+ : undefined;
586
+ }
587
+ export function parseGoalStepStatus(value) {
588
+ return value === "pending" || value === "in_progress" || value === "completed" || value === "blocked" || value === "skipped"
589
+ ? value
590
+ : undefined;
591
+ }
592
+ function numeric(value) {
593
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
594
+ }
595
+ function numericOrUndefined(value) {
596
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
597
+ }
598
+ function durationMsFromGoalData(candidate) {
599
+ const millis = numeric(candidate.time_used_ms);
600
+ if (millis > 0 || typeof candidate.time_used_ms === "number") {
601
+ return millis;
602
+ }
603
+ return numeric(candidate.time_used_seconds) * 1000;
604
+ }
605
+ function optionalString(value) {
606
+ if (typeof value !== "string") {
607
+ return undefined;
608
+ }
609
+ const trimmed = value.trim();
610
+ return trimmed ? trimmed : undefined;
611
+ }
612
+ function cleanOptionalString(value) {
613
+ return optionalString(value);
614
+ }
615
+ function cleanPlanStepTitle(value) {
616
+ const trimmed = value
617
+ ?.replace(/`([^`]+)`/g, "$1")
618
+ .replace(/\*\*([^*]+)\*\*/g, "$1")
619
+ .replace(/\*([^*]+)\*/g, "$1")
620
+ .trim();
621
+ return trimmed || undefined;
622
+ }
623
+ function parseGoalPlanningStepLine(line) {
624
+ const match = /^\s*(?:[-*+]|\d+[.)])\s+(?:\[(?<mark>[ xX-])\]\s*)?(?<title>.+?)\s*$/.exec(line);
625
+ const title = cleanPlanStepTitle(match?.groups?.title);
626
+ if (!title) {
627
+ return undefined;
628
+ }
629
+ const status = goalStepStatusFromCheckbox(match?.groups?.mark);
630
+ return status ? { title, status } : { title };
631
+ }
632
+ function goalStepStatusFromCheckbox(value) {
633
+ if (value === "x" || value === "X") {
634
+ return "completed";
635
+ }
636
+ if (value === "-") {
637
+ return "skipped";
638
+ }
639
+ if (value === " ") {
640
+ return "pending";
641
+ }
642
+ return undefined;
643
+ }
644
+ function goalStepTitleKey(value) {
645
+ return value.toLowerCase().replace(/\s+/g, " ").trim();
646
+ }
647
+ function cloneGoalPlanning(planning) {
648
+ return {
649
+ summary: planning.summary,
650
+ active_step_id: planning.active_step_id,
651
+ updated_at: planning.updated_at,
652
+ steps: planning.steps.map((step) => ({
653
+ ...step,
654
+ evidence: step.evidence ? cloneJsonObject(step.evidence) : undefined,
655
+ })),
656
+ };
657
+ }
658
+ function cloneJsonObject(value) {
659
+ return JSON.parse(JSON.stringify(value));
660
+ }
661
+ function parseJsonObject(value) {
662
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
663
+ return undefined;
664
+ }
665
+ return cloneJsonObject(value);
666
+ }
667
+ function normalizeGoalStepId(rawId, title, index, used) {
668
+ const seed = rawId?.trim() || title.trim() || `step-${index + 1}`;
669
+ const base = seed
670
+ .toLowerCase()
671
+ .replace(/[^a-z0-9_.:-]+/g, "-")
672
+ .replace(/^-+|-+$/g, "")
673
+ .slice(0, 64) || `step-${index + 1}`;
674
+ let candidate = base;
675
+ let suffix = 2;
676
+ while (used.has(candidate)) {
677
+ candidate = `${base}-${suffix}`;
678
+ suffix += 1;
679
+ }
680
+ return candidate;
681
+ }
682
+ function normalizeExistingStepId(stepId, steps) {
683
+ const trimmed = stepId?.trim();
684
+ if (!trimmed) {
685
+ return undefined;
686
+ }
687
+ return steps.some((step) => step.id === trimmed) ? trimmed : undefined;
688
+ }
689
+ function firstNonTerminalStep(steps) {
690
+ return steps.find((step) => !isTerminalGoalStepStatus(step.status));
691
+ }
692
+ function isTerminalGoalStepStatus(status) {
693
+ return status === "completed" || status === "skipped";
694
+ }
695
+ function countPart(counts, status, label) {
696
+ const count = counts.get(status) ?? 0;
697
+ return count > 0 ? `${count} ${label}` : undefined;
698
+ }
699
+ function countLabel(count, singular) {
700
+ return `${count} ${singular}${count === 1 ? "" : "s"}`;
701
+ }
702
+ function formatDurationMs(durationMs) {
703
+ const safe = Math.max(0, Math.trunc(durationMs));
704
+ if (safe > 0 && safe < 1000) {
705
+ return `${safe}ms`;
706
+ }
707
+ return formatSeconds(Math.floor(safe / 1000));
708
+ }
709
+ function formatSeconds(seconds) {
710
+ if (seconds < 60) {
711
+ return `${seconds}s`;
712
+ }
713
+ const minutes = Math.floor(seconds / 60);
714
+ const remainder = seconds % 60;
715
+ if (minutes < 60) {
716
+ return remainder ? `${minutes}m ${remainder}s` : `${minutes}m`;
717
+ }
718
+ const hours = Math.floor(minutes / 60);
719
+ const minuteRemainder = minutes % 60;
720
+ return minuteRemainder ? `${hours}h ${minuteRemainder}m` : `${hours}h`;
721
+ }
722
+ function stepMarker(status) {
723
+ switch (status) {
724
+ case "completed":
725
+ return "x";
726
+ case "in_progress":
727
+ return "*";
728
+ case "blocked":
729
+ return "!";
730
+ case "skipped":
731
+ return "-";
732
+ case "pending":
733
+ return " ";
734
+ }
735
+ }
736
+ //# sourceMappingURL=state.js.map