@makefinks/daemon 0.1.4 → 0.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 (34) hide show
  1. package/package.json +5 -4
  2. package/src/ai/daemon-ai.ts +30 -85
  3. package/src/ai/system-prompt.ts +134 -111
  4. package/src/ai/tool-approval-coordinator.ts +113 -0
  5. package/src/ai/tools/index.ts +12 -32
  6. package/src/ai/tools/subagents.ts +16 -30
  7. package/src/ai/tools/tool-registry.ts +203 -0
  8. package/src/app/App.tsx +23 -631
  9. package/src/app/components/AppOverlays.tsx +25 -1
  10. package/src/app/components/ConversationPane.tsx +5 -3
  11. package/src/components/HotkeysPane.tsx +3 -1
  12. package/src/components/TokenUsageDisplay.tsx +11 -11
  13. package/src/components/ToolsMenu.tsx +235 -0
  14. package/src/components/UrlMenu.tsx +182 -0
  15. package/src/hooks/daemon-event-handlers/interrupted-turn.ts +148 -0
  16. package/src/hooks/daemon-event-handlers.ts +11 -151
  17. package/src/hooks/use-app-context-builder.ts +4 -0
  18. package/src/hooks/use-app-controller.ts +546 -0
  19. package/src/hooks/use-app-menus.ts +12 -0
  20. package/src/hooks/use-app-preferences-bootstrap.ts +9 -0
  21. package/src/hooks/use-bootstrap-controller.ts +92 -0
  22. package/src/hooks/use-daemon-keyboard.ts +63 -57
  23. package/src/hooks/use-daemon-runtime-controller.ts +147 -0
  24. package/src/hooks/use-grounding-menu-controller.ts +51 -0
  25. package/src/hooks/use-overlay-controller.ts +84 -0
  26. package/src/hooks/use-session-controller.ts +79 -0
  27. package/src/hooks/use-url-menu-items.ts +19 -0
  28. package/src/state/app-context.tsx +4 -0
  29. package/src/state/daemon-state.ts +19 -8
  30. package/src/state/session-store.ts +4 -0
  31. package/src/types/index.ts +39 -0
  32. package/src/utils/derive-url-menu-items.ts +155 -0
  33. package/src/utils/formatters.ts +1 -7
  34. package/src/utils/preferences.ts +10 -0
@@ -24,6 +24,15 @@ import { REASONING_COLORS, STATE_COLORS } from "../types/theme";
24
24
  import { REASONING_ANIMATION } from "../ui/constants";
25
25
  import { debug } from "../utils/debug-logger";
26
26
  import { hasVisibleText } from "../utils/formatters";
27
+ import {
28
+ INTERRUPTED_TOOL_RESULT,
29
+ buildInterruptedContentBlocks,
30
+ buildInterruptedModelMessages,
31
+ normalizeInterruptedToolBlockResult,
32
+ normalizeInterruptedToolResultOutput,
33
+ } from "./daemon-event-handlers/interrupted-turn";
34
+
35
+ export { buildInterruptedModelMessages };
27
36
 
28
37
  function getToolCategory(toolName: string): ToolCategory | "fast" | undefined {
29
38
  if (toolName === "subagent") return "subagent";
@@ -39,30 +48,6 @@ function getToolCategory(toolName: string): ToolCategory | "fast" | undefined {
39
48
  return undefined;
40
49
  }
41
50
 
42
- const INTERRUPTED_TOOL_RESULT = "Tool execution interrupted by user";
43
-
44
- function normalizeInterruptedToolBlockResult(result: unknown): unknown {
45
- if (result !== undefined) return result;
46
- return { success: false, error: INTERRUPTED_TOOL_RESULT };
47
- }
48
-
49
- function normalizeInterruptedToolResultOutput(result: unknown): ToolResultOutput {
50
- if (result === undefined) {
51
- return { type: "error-text", value: INTERRUPTED_TOOL_RESULT };
52
- }
53
-
54
- if (typeof result === "string") {
55
- return { type: "text", value: result };
56
- }
57
-
58
- try {
59
- JSON.stringify(result);
60
- return { type: "json", value: result as ToolResultOutput["value"] };
61
- } catch {
62
- return { type: "text", value: String(result) };
63
- }
64
- }
65
-
66
51
  function clearAvatarToolEffects(avatar: DaemonAvatarRenderable | null): void {
67
52
  if (!avatar) return;
68
53
  avatar.triggerToolComplete();
@@ -71,133 +56,6 @@ function clearAvatarToolEffects(avatar: DaemonAvatarRenderable | null): void {
71
56
  avatar.setTypingMode(false);
72
57
  }
73
58
 
74
- function buildInterruptedContentBlocks(contentBlocks: ContentBlock[]): ContentBlock[] {
75
- return contentBlocks.map((block) => {
76
- if (block.type !== "tool") return { ...block };
77
-
78
- const call = { ...block.call };
79
- if (call.status === "running") {
80
- call.status = "failed";
81
- call.error = INTERRUPTED_TOOL_RESULT;
82
- }
83
- if (call.subagentSteps) {
84
- call.subagentSteps = call.subagentSteps.map((step) =>
85
- step.status === "running" ? { ...step, status: "failed" } : step
86
- );
87
- }
88
-
89
- return {
90
- ...block,
91
- call,
92
- result: normalizeInterruptedToolBlockResult(block.result),
93
- };
94
- });
95
- }
96
-
97
- export function buildInterruptedModelMessages(contentBlocks: ContentBlock[]): ModelMessage[] {
98
- const messages: ModelMessage[] = [];
99
-
100
- type AssistantPart =
101
- | { type: "text"; text: string }
102
- | { type: "reasoning"; text: string }
103
- | { type: "tool-call"; toolCallId: string; toolName: string; input: unknown };
104
-
105
- type ToolResultPart = {
106
- type: "tool-result";
107
- toolCallId: string;
108
- toolName: string;
109
- output: ToolResultOutput;
110
- };
111
-
112
- let assistantParts: AssistantPart[] = [];
113
- let toolResults: ToolResultPart[] = [];
114
-
115
- for (const block of contentBlocks) {
116
- if (block.type === "reasoning" && block.content) {
117
- // Tool results must be emitted before new assistant reasoning.
118
- if (toolResults.length > 0) {
119
- messages.push({
120
- role: "tool",
121
- content: [...toolResults],
122
- } as unknown as ModelMessage);
123
- toolResults = [];
124
- }
125
-
126
- assistantParts.push({ type: "reasoning", text: block.content });
127
- continue;
128
- }
129
-
130
- if (block.type === "text" && block.content) {
131
- // Tool results must be emitted before new assistant text.
132
- if (toolResults.length > 0) {
133
- messages.push({
134
- role: "tool",
135
- content: [...toolResults],
136
- } as unknown as ModelMessage);
137
- toolResults = [];
138
- }
139
-
140
- assistantParts.push({ type: "text", text: block.content });
141
- continue;
142
- }
143
-
144
- if (block.type === "tool") {
145
- // Tool results must be emitted before a new tool call.
146
- if (toolResults.length > 0) {
147
- messages.push({
148
- role: "tool",
149
- content: [...toolResults],
150
- } as unknown as ModelMessage);
151
- toolResults = [];
152
- }
153
-
154
- const toolCallId = block.call.toolCallId;
155
- if (!toolCallId) {
156
- continue;
157
- }
158
-
159
- assistantParts.push({
160
- type: "tool-call",
161
- toolCallId,
162
- toolName: block.call.name,
163
- input: block.call.input ?? {},
164
- });
165
-
166
- // Tool calls must be emitted before their tool results.
167
- if (assistantParts.length > 0) {
168
- messages.push({
169
- role: "assistant",
170
- content: [...assistantParts],
171
- } as unknown as ModelMessage);
172
- assistantParts = [];
173
- }
174
-
175
- toolResults.push({
176
- type: "tool-result",
177
- toolCallId,
178
- toolName: block.call.name,
179
- output: normalizeInterruptedToolResultOutput(block.result),
180
- });
181
- }
182
- }
183
-
184
- if (assistantParts.length > 0) {
185
- messages.push({
186
- role: "assistant",
187
- content: [...assistantParts],
188
- } as unknown as ModelMessage);
189
- }
190
-
191
- if (toolResults.length > 0) {
192
- messages.push({
193
- role: "tool",
194
- content: [...toolResults],
195
- } as unknown as ModelMessage);
196
- }
197
-
198
- return messages;
199
- }
200
-
201
59
  function finalizePendingUserMessage(
202
60
  prev: ConversationMessage[],
203
61
  userText: string,
@@ -725,6 +583,8 @@ function mergeTokenUsage(prev: TokenUsage, usage: TokenUsage, isSubagent: boolea
725
583
  subagentTotalTokens: prev.subagentTotalTokens,
726
584
  subagentPromptTokens: prev.subagentPromptTokens,
727
585
  subagentCompletionTokens: prev.subagentCompletionTokens,
586
+ latestTurnPromptTokens: usage.promptTokens,
587
+ latestTurnCompletionTokens: usage.completionTokens,
728
588
  };
729
589
  }
730
590
 
@@ -39,6 +39,10 @@ export interface UseAppContextBuilderParams {
39
39
  setShowHotkeysPane: React.Dispatch<React.SetStateAction<boolean>>;
40
40
  showGroundingMenu: boolean;
41
41
  setShowGroundingMenu: React.Dispatch<React.SetStateAction<boolean>>;
42
+ showUrlMenu: boolean;
43
+ setShowUrlMenu: React.Dispatch<React.SetStateAction<boolean>>;
44
+ showToolsMenu: boolean;
45
+ setShowToolsMenu: React.Dispatch<React.SetStateAction<boolean>>;
42
46
  };
43
47
 
44
48
  device: {