@bubblebrain-ai/bubble 0.0.3 → 0.0.5
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.
- package/README.md +8 -3
- package/dist/agent/budget-ledger.d.ts +20 -0
- package/dist/agent/budget-ledger.js +51 -0
- package/dist/agent/execution-governor.d.ts +14 -0
- package/dist/agent/execution-governor.js +172 -14
- package/dist/agent/profiles.d.ts +59 -0
- package/dist/agent/profiles.js +460 -0
- package/dist/agent/subagent-control.d.ts +52 -0
- package/dist/agent/subagent-control.js +38 -0
- package/dist/agent/task-classifier.d.ts +1 -1
- package/dist/agent/task-classifier.js +60 -0
- package/dist/agent/tool-intent.d.ts +14 -0
- package/dist/agent/tool-intent.js +125 -1
- package/dist/agent.d.ts +60 -1
- package/dist/agent.js +606 -53
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +45 -0
- package/dist/context/budget.js +1 -0
- package/dist/context/compact-llm.js +7 -6
- package/dist/context/compact.js +6 -6
- package/dist/context/projector.d.ts +3 -3
- package/dist/context/projector.js +32 -18
- package/dist/context/prune.d.ts +2 -2
- package/dist/context/prune.js +1 -4
- package/dist/main.d.ts +1 -1
- package/dist/main.js +13 -6
- package/dist/mcp/manager.js +1 -0
- package/dist/orchestrator/default-hooks.js +92 -1
- package/dist/orchestrator/hooks.d.ts +10 -0
- package/dist/prompt/compose.d.ts +1 -0
- package/dist/prompt/compose.js +20 -1
- package/dist/prompt/environment.js +21 -2
- package/dist/prompt/provider-prompts/deepseek.d.ts +1 -0
- package/dist/prompt/provider-prompts/deepseek.js +8 -0
- package/dist/prompt/provider-prompts/glm.d.ts +1 -0
- package/dist/prompt/provider-prompts/glm.js +7 -0
- package/dist/prompt/provider-prompts/kimi.d.ts +1 -0
- package/dist/prompt/provider-prompts/kimi.js +7 -0
- package/dist/prompt/reminders.d.ts +5 -1
- package/dist/prompt/reminders.js +51 -6
- package/dist/prompt/runtime.d.ts +1 -1
- package/dist/prompt/runtime.js +16 -3
- package/dist/prompt/task-reminders.d.ts +2 -0
- package/dist/prompt/task-reminders.js +56 -0
- package/dist/provider-artifacts.d.ts +7 -0
- package/dist/provider-artifacts.js +60 -0
- package/dist/provider.d.ts +6 -7
- package/dist/provider.js +77 -15
- package/dist/session-log.js +3 -1
- package/dist/slash-commands/commands.js +2 -3
- package/dist/system-prompt.d.ts +2 -0
- package/dist/tools/agent-lifecycle.d.ts +6 -0
- package/dist/tools/agent-lifecycle.js +355 -0
- package/dist/tools/bash.js +12 -7
- package/dist/tools/edit-apply.d.ts +25 -0
- package/dist/tools/edit-apply.js +197 -0
- package/dist/tools/edit.js +64 -52
- package/dist/tools/exit-plan-mode.js +3 -1
- package/dist/tools/file-mutation-queue.d.ts +1 -0
- package/dist/tools/file-mutation-queue.js +32 -0
- package/dist/tools/glob.js +1 -0
- package/dist/tools/grep.js +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +3 -3
- package/dist/tools/lsp.js +2 -0
- package/dist/tools/memory.js +2 -0
- package/dist/tools/question.js +2 -0
- package/dist/tools/read.js +1 -0
- package/dist/tools/skill.js +1 -0
- package/dist/tools/task.js +1 -0
- package/dist/tools/todo.js +1 -0
- package/dist/tools/tool-search.js +2 -1
- package/dist/tools/web-fetch.js +1 -0
- package/dist/tools/web-search.js +1 -0
- package/dist/tools/write.js +10 -1
- package/dist/tui/display-history.d.ts +8 -1
- package/dist/tui/image-paste.d.ts +41 -0
- package/dist/tui/image-paste.js +217 -0
- package/dist/tui/markdown-inline.d.ts +22 -0
- package/dist/tui/markdown-inline.js +68 -0
- package/dist/tui/render-signature.d.ts +1 -0
- package/dist/tui/render-signature.js +7 -0
- package/dist/tui/run.js +814 -269
- package/dist/tui/tool-renderers/fallback.d.ts +2 -0
- package/dist/tui/tool-renderers/fallback.js +75 -0
- package/dist/tui/tool-renderers/registry.d.ts +3 -0
- package/dist/tui/tool-renderers/registry.js +11 -0
- package/dist/tui/tool-renderers/subagent.d.ts +2 -0
- package/dist/tui/tool-renderers/subagent.js +114 -0
- package/dist/tui/tool-renderers/types.d.ts +36 -0
- package/dist/tui/tool-renderers/types.js +1 -0
- package/dist/tui/tool-renderers/write-preview.d.ts +12 -0
- package/dist/tui/tool-renderers/write-preview.js +22 -0
- package/dist/tui/tool-renderers/write.d.ts +6 -0
- package/dist/tui/tool-renderers/write.js +82 -0
- package/dist/types.d.ts +90 -10
- package/package.json +3 -3
package/dist/prompt/reminders.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* System reminders - short, runtime-variable instructions injected into the
|
|
3
|
-
* message stream as
|
|
3
|
+
* message stream as hidden meta messages.
|
|
4
4
|
*
|
|
5
5
|
* Rationale: the static system prompt is stable and cacheable. Mode transitions
|
|
6
6
|
* and other ephemeral state are signaled via reminders so we do not invalidate
|
|
7
7
|
* the prompt cache every time something changes.
|
|
8
8
|
*/
|
|
9
9
|
export function wrapInSystemReminder(content) {
|
|
10
|
-
return
|
|
10
|
+
return content.trim();
|
|
11
11
|
}
|
|
12
12
|
export function isPermissionModeReminder(content) {
|
|
13
13
|
if (typeof content !== "string")
|
|
@@ -20,8 +20,9 @@ const PLAN_MODE_ENTER = `
|
|
|
20
20
|
Plan mode is now ACTIVE.
|
|
21
21
|
|
|
22
22
|
Rules while in plan mode:
|
|
23
|
-
- Only read-only tools are allowed, including read, glob, grep, lsp, web_search, web_fetch,
|
|
23
|
+
- Only read-only tools are allowed, including read, glob, grep, lsp, web_search, web_fetch, spawn_agent, wait_agent, send_input, close_agent, skill, todo_write, tool_search, question, and exit_plan_mode.
|
|
24
24
|
- Writes, edits, and shell commands WILL be rejected by the harness; do not try them.
|
|
25
|
+
- Do not edit files or claim implementation is complete while plan mode is active.
|
|
25
26
|
- Investigate the codebase, then use the question tool to clarify important ambiguities, tradeoffs, requirements, or preference choices that would materially change the plan.
|
|
26
27
|
- Call exit_plan_mode with a concrete step-by-step plan after the important questions are resolved.
|
|
27
28
|
- Do not use the question tool to ask whether the plan is approved; exit_plan_mode is the approval step.
|
|
@@ -34,11 +35,13 @@ Permission mode is now: bypassPermissions.
|
|
|
34
35
|
ALL tool calls auto-approve with no user confirmation. The user has explicitly opted into this.
|
|
35
36
|
Proceed with extra care — explain risky actions in the chat BEFORE performing them, and
|
|
36
37
|
prefer reversible operations when possible.
|
|
38
|
+
Do not perform destructive operations, credential exposure, or unrelated reversions just because approvals are bypassed.
|
|
37
39
|
`;
|
|
38
40
|
const DEFAULT_ENTER = `
|
|
39
41
|
Permission mode is now: default Build mode.
|
|
40
42
|
|
|
41
43
|
File edits and writes auto-approve. Bash commands and other destructive tools still require explicit approval unless allowed by rules.
|
|
44
|
+
Execute the requested change end to end; do not stop at analysis unless blocked or the user explicitly asks for discussion only.
|
|
42
45
|
`;
|
|
43
46
|
/** Picks the correct reminder text for a transition TO a given mode. */
|
|
44
47
|
export function reminderForMode(mode) {
|
|
@@ -52,7 +55,6 @@ export function reminderForMode(mode) {
|
|
|
52
55
|
return wrapInSystemReminder(DEFAULT_ENTER);
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
|
-
// Backward-compat exports kept in case external code pinned the old names.
|
|
56
58
|
export const PLAN_MODE_ENTER_REMINDER = reminderForMode("plan");
|
|
57
59
|
export const PLAN_MODE_EXIT_REMINDER = reminderForMode("default");
|
|
58
60
|
/**
|
|
@@ -86,11 +88,11 @@ Stop once these categories are covered. Do not keep repeating near-identical sea
|
|
|
86
88
|
}
|
|
87
89
|
export function buildLoopWarningReminder(reason) {
|
|
88
90
|
return wrapInSystemReminder(`
|
|
89
|
-
|
|
91
|
+
Tool loop warning.
|
|
90
92
|
|
|
91
93
|
${reason}
|
|
92
94
|
|
|
93
|
-
Do not repeat near-identical
|
|
95
|
+
Do not repeat near-identical reads or searches unless you are changing the path or testing a genuinely new hypothesis.
|
|
94
96
|
If current evidence is sufficient, summarize your findings now.
|
|
95
97
|
`);
|
|
96
98
|
}
|
|
@@ -104,6 +106,19 @@ Do not continue blind keyword searching. Use the evidence already gathered to re
|
|
|
104
106
|
You may still read specific files if you already know where the relevant configuration or persistence logic lives.
|
|
105
107
|
`);
|
|
106
108
|
}
|
|
109
|
+
export function buildExplorationFreezeReminder(reason) {
|
|
110
|
+
return wrapInSystemReminder(`
|
|
111
|
+
Implementation phase has advanced from exploration to modification.
|
|
112
|
+
|
|
113
|
+
Reason: ${reason}
|
|
114
|
+
|
|
115
|
+
You have enough context to act. Do not continue reading, searching, or delegating exploration.
|
|
116
|
+
Choose one of:
|
|
117
|
+
1. Use edit/write to make the requested change.
|
|
118
|
+
2. If no safe change can be made from the gathered context, explain the concrete blocker.
|
|
119
|
+
3. If files were already changed, run the narrowest meaningful verification or finish with the result.
|
|
120
|
+
`);
|
|
121
|
+
}
|
|
107
122
|
export function buildToolFreezeReminder(reason) {
|
|
108
123
|
return wrapInSystemReminder(`
|
|
109
124
|
CRITICAL - MAXIMUM STEPS REACHED
|
|
@@ -156,3 +171,33 @@ Treat the task output as a bounded subtask result:
|
|
|
156
171
|
- do not re-run the same exploratory search unless the subtask uncovered a concrete contradiction
|
|
157
172
|
`);
|
|
158
173
|
}
|
|
174
|
+
export function buildVerificationReminder(reason) {
|
|
175
|
+
return wrapInSystemReminder(`
|
|
176
|
+
Verification required before final answer.
|
|
177
|
+
|
|
178
|
+
${reason}
|
|
179
|
+
|
|
180
|
+
You have changed files in this turn. Run the narrowest meaningful verification command or runtime check before finalizing.
|
|
181
|
+
If verification truly cannot be run, state the concrete blocker and the residual risk.
|
|
182
|
+
`);
|
|
183
|
+
}
|
|
184
|
+
export function buildVerificationFailureReminder(reason) {
|
|
185
|
+
return wrapInSystemReminder(`
|
|
186
|
+
Verification failed after file changes.
|
|
187
|
+
|
|
188
|
+
${reason}
|
|
189
|
+
|
|
190
|
+
Do not finalize as complete while this failure is unresolved. Make one focused fix and rerun the most relevant verification.
|
|
191
|
+
If you cannot fix it, explain the concrete blocker and the residual risk instead of claiming success.
|
|
192
|
+
`);
|
|
193
|
+
}
|
|
194
|
+
export function buildFinalizeOpportunityReminder(reason) {
|
|
195
|
+
return wrapInSystemReminder(`
|
|
196
|
+
Completion checkpoint.
|
|
197
|
+
|
|
198
|
+
${reason}
|
|
199
|
+
|
|
200
|
+
If this satisfies the user's request, provide the final answer now.
|
|
201
|
+
Continue using tools only if there is a concrete remaining requirement, failing check, or missing deliverable.
|
|
202
|
+
`);
|
|
203
|
+
}
|
package/dist/prompt/runtime.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export interface RuntimePromptOptions {
|
|
|
3
3
|
thinkingLevel?: ThinkingLevel;
|
|
4
4
|
/**
|
|
5
5
|
* Kept for API compatibility. Agent mode is no longer baked into the static
|
|
6
|
-
* system prompt — mode changes are signalled via
|
|
6
|
+
* system prompt — mode changes are signalled via hidden runtime reminders
|
|
7
7
|
* (see src/prompt/reminders.ts) so the base prompt stays stable for caching.
|
|
8
8
|
*/
|
|
9
9
|
mode?: PermissionMode;
|
package/dist/prompt/runtime.js
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
const defaultGuidelines = [
|
|
2
|
+
"Inspect relevant files, command output, or runtime state before making claims about code behavior",
|
|
3
|
+
"Separate confirmed facts from inference when the evidence is incomplete",
|
|
4
|
+
"Prefer runtime and call-chain evidence over README text or configuration names for behavior questions",
|
|
2
5
|
"Before editing or writing files, read them first if they exist",
|
|
3
6
|
"Use edit for targeted changes to existing files; use write for creating new files",
|
|
4
|
-
"
|
|
5
|
-
"Show file paths clearly when working with files",
|
|
7
|
+
"Edit only the files required for the requested change",
|
|
6
8
|
"Prefer structured search tools over bash for repository searches whenever possible",
|
|
7
9
|
"Do not repeat near-identical searches when they are not producing new evidence",
|
|
8
10
|
"When investigating configuration or security questions, stop once the relevant load path, storage path, and exposure path are identified",
|
|
9
|
-
"Use
|
|
11
|
+
"Use spawn_agent and wait_agent for bounded investigative subproblems instead of letting the main loop churn on repeated exploratory searches",
|
|
12
|
+
"After code edits, run the narrowest meaningful verification command or explain why verification is not possible",
|
|
13
|
+
"When finishing a coding task, report what changed, where it changed, verification results, and remaining risk",
|
|
14
|
+
"Be concise in your responses",
|
|
10
15
|
];
|
|
11
16
|
export function buildRuntimePrompt(options = {}) {
|
|
12
17
|
const thinkingLevel = options.thinkingLevel ?? "off";
|
|
13
18
|
const guidelines = dedupe(defaultGuidelines, options.guidelines ?? []);
|
|
14
19
|
return `Current thinking level: ${thinkingLevel}
|
|
15
20
|
|
|
21
|
+
Execution protocol:
|
|
22
|
+
1. Understand the user's requested outcome and current constraints.
|
|
23
|
+
2. Inspect the relevant files or state before making claims or edits.
|
|
24
|
+
3. Choose the smallest coherent change that solves the actual problem.
|
|
25
|
+
4. Edit only the necessary files.
|
|
26
|
+
5. Verify with the narrowest meaningful command or runtime check when possible.
|
|
27
|
+
6. Finish with changed files, verification results, and unresolved risk.
|
|
28
|
+
|
|
16
29
|
Guidelines:
|
|
17
30
|
${guidelines.map((item) => `- ${item}`).join("\n")}`;
|
|
18
31
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { wrapInSystemReminder } from "./reminders.js";
|
|
2
|
+
export function reminderForTaskType(taskType) {
|
|
3
|
+
switch (taskType) {
|
|
4
|
+
case "debugging":
|
|
5
|
+
return wrapInSystemReminder(`
|
|
6
|
+
Debugging workflow:
|
|
7
|
+
- Reproduce or identify the failing boundary before editing.
|
|
8
|
+
- Trace input, transformation, and output paths.
|
|
9
|
+
- Prefer fixing the mechanism over raising thresholds or adding superficial fallbacks.
|
|
10
|
+
- Verify the specific failure path after the change.
|
|
11
|
+
`);
|
|
12
|
+
case "implementation":
|
|
13
|
+
return wrapInSystemReminder(`
|
|
14
|
+
Implementation workflow:
|
|
15
|
+
- Do not stop at a proposal when the user asked for a change.
|
|
16
|
+
- Inspect the relevant files first, then make the smallest coherent edit.
|
|
17
|
+
- Keep unrelated files and behavior out of scope.
|
|
18
|
+
- Run a narrow verification command or explain why it cannot be run.
|
|
19
|
+
`);
|
|
20
|
+
case "code_review":
|
|
21
|
+
return wrapInSystemReminder(`
|
|
22
|
+
Code review workflow:
|
|
23
|
+
- Lead with concrete findings, ordered by severity.
|
|
24
|
+
- Reference file paths and line numbers when possible.
|
|
25
|
+
- Prioritize bugs, regressions, missing tests, security, and user-visible risk.
|
|
26
|
+
- Keep summaries secondary to findings.
|
|
27
|
+
`);
|
|
28
|
+
case "code_explanation":
|
|
29
|
+
return wrapInSystemReminder(`
|
|
30
|
+
Code explanation workflow:
|
|
31
|
+
- Answer the direct question first.
|
|
32
|
+
- Ground claims in concrete files, functions, and call paths.
|
|
33
|
+
- Distinguish current source evidence from inference.
|
|
34
|
+
- Avoid proposing changes unless the user asks for them.
|
|
35
|
+
`);
|
|
36
|
+
case "repo_orientation":
|
|
37
|
+
return wrapInSystemReminder(`
|
|
38
|
+
Repository orientation workflow:
|
|
39
|
+
- Start with the repo purpose and main execution paths.
|
|
40
|
+
- Inspect README/package metadata plus core runtime files before summarizing.
|
|
41
|
+
- Keep the first pass read-only unless the user asks for changes or runtime verification.
|
|
42
|
+
`);
|
|
43
|
+
case "product_discussion":
|
|
44
|
+
return wrapInSystemReminder(`
|
|
45
|
+
Product discussion workflow:
|
|
46
|
+
- Clarify the product goal, user workflow, and tradeoffs before suggesting implementation.
|
|
47
|
+
- Give direct product judgment when the user asks for direction.
|
|
48
|
+
- Avoid drifting into code changes unless the user explicitly asks to execute.
|
|
49
|
+
`);
|
|
50
|
+
case "security_investigation":
|
|
51
|
+
case "code_search":
|
|
52
|
+
case "general":
|
|
53
|
+
default:
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function stripProviderProtocolArtifacts(text: string): string;
|
|
2
|
+
export declare function isOnlyProviderProtocolArtifacts(text: string): boolean;
|
|
3
|
+
export interface ProviderProtocolArtifactFilter {
|
|
4
|
+
push(text: string): string;
|
|
5
|
+
flush(): string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createProviderProtocolArtifactFilter(): ProviderProtocolArtifactFilter;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Models with non-OpenAI chat templates (GLM-4.5/4.6, DeepSeek, some Kimi builds)
|
|
2
|
+
// emit tool-call delimiters as inline assistant text instead of as structured
|
|
3
|
+
// tool_calls deltas. The shapes vary — `<|tool_call|>`, `<||DSML||tool_calls>`,
|
|
4
|
+
// `<||DSML||invoke name="x">`, closing `</||DSML||tool_calls>`, etc. — but
|
|
5
|
+
// they always share the pattern of a tag whose name is wrapped in `|` or `|`.
|
|
6
|
+
// If we let any of that text reach the consumer it pollutes the streamed
|
|
7
|
+
// assistant text and, downstream, the subagent's summary field.
|
|
8
|
+
const TOOL_PROTOCOL_PATTERNS = [
|
|
9
|
+
// Generic: opening or closing tag whose name is wrapped in `|` or `|`,
|
|
10
|
+
// optionally with attributes after the closing pipe (e.g. `invoke name="x"`).
|
|
11
|
+
/<\/?\s*[||]+[^<>]*?[||]+[^<>]*>/g,
|
|
12
|
+
// Plain ASCII variants without attributes.
|
|
13
|
+
/<\/?\|tool_calls?\|>/gi,
|
|
14
|
+
];
|
|
15
|
+
export function stripProviderProtocolArtifacts(text) {
|
|
16
|
+
let out = text;
|
|
17
|
+
for (const pattern of TOOL_PROTOCOL_PATTERNS) {
|
|
18
|
+
out = out.replace(pattern, "");
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
export function isOnlyProviderProtocolArtifacts(text) {
|
|
23
|
+
return !!text.trim() && stripProviderProtocolArtifacts(text).trim().length === 0;
|
|
24
|
+
}
|
|
25
|
+
export function createProviderProtocolArtifactFilter() {
|
|
26
|
+
let pending = "";
|
|
27
|
+
return {
|
|
28
|
+
push(text) {
|
|
29
|
+
pending = stripProviderProtocolArtifacts(pending + text);
|
|
30
|
+
const keep = trailingPossibleMarkerLength(pending);
|
|
31
|
+
const emit = pending.slice(0, pending.length - keep);
|
|
32
|
+
pending = pending.slice(pending.length - keep);
|
|
33
|
+
return emit;
|
|
34
|
+
},
|
|
35
|
+
flush() {
|
|
36
|
+
const out = stripProviderProtocolArtifacts(pending);
|
|
37
|
+
pending = "";
|
|
38
|
+
return out;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Hold back a trailing fragment if it could be the start of a pipe-wrapped tag
|
|
43
|
+
// whose closing `>` hasn't arrived yet. Without this guard, a stream that flushes
|
|
44
|
+
// mid-tag (`<` ... `|DSML|tool_calls`) would emit the partial tag as text, then
|
|
45
|
+
// emit the rest later — the stripping regex only matches complete tags.
|
|
46
|
+
function trailingPossibleMarkerLength(text) {
|
|
47
|
+
const lastLt = text.lastIndexOf("<");
|
|
48
|
+
if (lastLt === -1)
|
|
49
|
+
return 0;
|
|
50
|
+
const tail = text.slice(lastLt);
|
|
51
|
+
if (tail.includes(">"))
|
|
52
|
+
return 0;
|
|
53
|
+
// Hold back only when the trailing fragment looks like the start of a protocol
|
|
54
|
+
// tag. Anything else (e.g. `if (x < y)` in source) flushes immediately.
|
|
55
|
+
if (/^<\/?$/.test(tail))
|
|
56
|
+
return tail.length;
|
|
57
|
+
if (/^<\/?\s*[||]/.test(tail))
|
|
58
|
+
return tail.length;
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
package/dist/provider.d.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Works with OpenRouter, OpenAI, DeepSeek, Google, Groq, Together, and local OpenAI-compatible endpoints.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { Provider, ProviderMessage, StreamChunk, ThinkingLevel } from "./types.js";
|
|
7
7
|
type ReasoningContentEcho = "tool_calls" | "all";
|
|
8
|
-
export declare function toChatCompletionsMessage(message:
|
|
8
|
+
export declare function toChatCompletionsMessage(message: ProviderMessage, options?: {
|
|
9
9
|
reasoningContentEcho?: ReasoningContentEcho;
|
|
10
10
|
}): Record<string, unknown>;
|
|
11
11
|
export interface ProviderInstanceOptions {
|
|
@@ -21,11 +21,10 @@ export declare function normalizeToolArgs(raw: string): string;
|
|
|
21
21
|
/**
|
|
22
22
|
* Convert an OpenAI-compatible chat-completions stream into our internal StreamChunk events.
|
|
23
23
|
*
|
|
24
|
-
* Multi-tool-call streams are
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* previous single-slot implementation silently dropped every call but the last.
|
|
24
|
+
* Multi-tool-call streams are tracked by `index`, but tool-call starts and
|
|
25
|
+
* argument deltas are emitted as soon as they arrive so the TUI can render
|
|
26
|
+
* partial write previews before the tool executes. End events are still flushed
|
|
27
|
+
* in index order to keep multi-call turns deterministic.
|
|
29
28
|
*/
|
|
30
29
|
export declare function translateOpenAIStream(stream: AsyncIterable<any>): AsyncIterable<StreamChunk>;
|
|
31
30
|
export {};
|
package/dist/provider.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import OpenAI from "openai";
|
|
7
7
|
import { createOpenAICodexProvider, isOpenAICodexBaseUrl } from "./provider-openai-codex.js";
|
|
8
|
+
import { createProviderProtocolArtifactFilter } from "./provider-artifacts.js";
|
|
8
9
|
import { resolveProviderRequestConfig } from "./provider-transform.js";
|
|
9
10
|
export function toChatCompletionsMessage(message, options = {}) {
|
|
10
11
|
const reasoningContentEcho = options.reasoningContentEcho ?? "tool_calls";
|
|
@@ -189,14 +190,14 @@ function extractBalancedJson(s, start) {
|
|
|
189
190
|
/**
|
|
190
191
|
* Convert an OpenAI-compatible chat-completions stream into our internal StreamChunk events.
|
|
191
192
|
*
|
|
192
|
-
* Multi-tool-call streams are
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* previous single-slot implementation silently dropped every call but the last.
|
|
193
|
+
* Multi-tool-call streams are tracked by `index`, but tool-call starts and
|
|
194
|
+
* argument deltas are emitted as soon as they arrive so the TUI can render
|
|
195
|
+
* partial write previews before the tool executes. End events are still flushed
|
|
196
|
+
* in index order to keep multi-call turns deterministic.
|
|
197
197
|
*/
|
|
198
198
|
export async function* translateOpenAIStream(stream) {
|
|
199
199
|
const toolCalls = new Map();
|
|
200
|
+
const textFilter = createProviderProtocolArtifactFilter();
|
|
200
201
|
function* flushToolCalls() {
|
|
201
202
|
if (toolCalls.size === 0)
|
|
202
203
|
return;
|
|
@@ -204,13 +205,34 @@ export async function* translateOpenAIStream(stream) {
|
|
|
204
205
|
for (const [, entry] of sorted) {
|
|
205
206
|
if (!entry.id || !entry.name)
|
|
206
207
|
continue;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
if (!entry.started) {
|
|
209
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: "", isStart: true, isEnd: false };
|
|
210
|
+
entry.started = true;
|
|
211
|
+
if (entry.args) {
|
|
212
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: entry.args, isStart: false, isEnd: false };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
yield {
|
|
216
|
+
type: "tool_call",
|
|
217
|
+
id: entry.id,
|
|
218
|
+
name: entry.name,
|
|
219
|
+
arguments: "",
|
|
220
|
+
argumentsFull: normalizeToolArgs(entry.args),
|
|
221
|
+
isStart: false,
|
|
222
|
+
isEnd: true,
|
|
223
|
+
};
|
|
211
224
|
}
|
|
212
225
|
toolCalls.clear();
|
|
213
226
|
}
|
|
227
|
+
function* startToolCallIfReady(entry) {
|
|
228
|
+
if (entry.started || !entry.id || !entry.name)
|
|
229
|
+
return;
|
|
230
|
+
entry.started = true;
|
|
231
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: "", isStart: true, isEnd: false };
|
|
232
|
+
if (entry.args) {
|
|
233
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: entry.args, isStart: false, isEnd: false };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
214
236
|
for await (const chunk of stream) {
|
|
215
237
|
const delta = chunk.choices?.[0]?.delta;
|
|
216
238
|
const usage = chunk.usage;
|
|
@@ -240,12 +262,16 @@ export async function* translateOpenAIStream(stream) {
|
|
|
240
262
|
yield { type: "reasoning_delta", content: thinkMatch[1] };
|
|
241
263
|
}
|
|
242
264
|
const remaining = delta.content.replace(/<think>[\s\S]*?<\/think>/, "");
|
|
243
|
-
|
|
244
|
-
|
|
265
|
+
const cleaned = textFilter.push(remaining);
|
|
266
|
+
if (cleaned) {
|
|
267
|
+
yield { type: "text", content: cleaned };
|
|
245
268
|
}
|
|
246
269
|
}
|
|
247
270
|
else {
|
|
248
|
-
|
|
271
|
+
const cleaned = textFilter.push(delta.content);
|
|
272
|
+
if (cleaned) {
|
|
273
|
+
yield { type: "text", content: cleaned };
|
|
274
|
+
}
|
|
249
275
|
}
|
|
250
276
|
}
|
|
251
277
|
if (delta?.tool_calls) {
|
|
@@ -253,15 +279,28 @@ export async function* translateOpenAIStream(stream) {
|
|
|
253
279
|
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
254
280
|
let entry = toolCalls.get(idx);
|
|
255
281
|
if (!entry) {
|
|
256
|
-
entry = { id: "", name: "", args: "" };
|
|
282
|
+
entry = { id: "", name: "", args: "", started: false };
|
|
257
283
|
toolCalls.set(idx, entry);
|
|
258
284
|
}
|
|
259
285
|
if (tc.id)
|
|
260
286
|
entry.id = tc.id;
|
|
261
287
|
if (tc.function?.name)
|
|
262
288
|
entry.name = tc.function.name;
|
|
263
|
-
|
|
264
|
-
|
|
289
|
+
yield* startToolCallIfReady(entry);
|
|
290
|
+
if (typeof tc.function?.arguments === "string" && tc.function.arguments) {
|
|
291
|
+
const merged = mergeToolArgumentDelta(entry.args, tc.function.arguments);
|
|
292
|
+
entry.args = merged.args;
|
|
293
|
+
if (entry.started && merged.delta) {
|
|
294
|
+
yield {
|
|
295
|
+
type: "tool_call",
|
|
296
|
+
id: entry.id,
|
|
297
|
+
name: entry.name,
|
|
298
|
+
arguments: merged.delta,
|
|
299
|
+
isStart: false,
|
|
300
|
+
isEnd: false,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
265
304
|
}
|
|
266
305
|
}
|
|
267
306
|
const finishReason = chunk.choices?.[0]?.finish_reason;
|
|
@@ -269,5 +308,28 @@ export async function* translateOpenAIStream(stream) {
|
|
|
269
308
|
yield* flushToolCalls();
|
|
270
309
|
}
|
|
271
310
|
}
|
|
311
|
+
const remainingText = textFilter.flush();
|
|
312
|
+
if (remainingText) {
|
|
313
|
+
yield { type: "text", content: remainingText };
|
|
314
|
+
}
|
|
272
315
|
yield* flushToolCalls();
|
|
273
316
|
}
|
|
317
|
+
function mergeToolArgumentDelta(current, incoming) {
|
|
318
|
+
if (!current)
|
|
319
|
+
return { args: incoming, delta: incoming };
|
|
320
|
+
if (!incoming)
|
|
321
|
+
return { args: current, delta: "" };
|
|
322
|
+
// Standard OpenAI-compatible streams send incremental argument deltas. Some
|
|
323
|
+
// providers send cumulative snapshots instead. If the incoming chunk already
|
|
324
|
+
// contains what we have, emit only the new suffix so downstream state remains
|
|
325
|
+
// append-only.
|
|
326
|
+
if (incoming.startsWith(current)) {
|
|
327
|
+
return { args: incoming, delta: incoming.slice(current.length) };
|
|
328
|
+
}
|
|
329
|
+
// Repeated identical snapshots should not duplicate the TUI preview or final
|
|
330
|
+
// JSON arguments.
|
|
331
|
+
if (incoming === current || current.endsWith(incoming)) {
|
|
332
|
+
return { args: current, delta: "" };
|
|
333
|
+
}
|
|
334
|
+
return { args: current + incoming, delta: incoming };
|
|
335
|
+
}
|
package/dist/session-log.js
CHANGED
|
@@ -193,6 +193,8 @@ function normalizeMessageToEntries(message, id, timestamp) {
|
|
|
193
193
|
}
|
|
194
194
|
case "tool":
|
|
195
195
|
return [{ id, type: "tool_result", message, timestamp }];
|
|
196
|
+
case "meta":
|
|
197
|
+
return [];
|
|
196
198
|
case "system":
|
|
197
199
|
return [{
|
|
198
200
|
id,
|
|
@@ -241,7 +243,7 @@ function pruneIncompleteTail(messages) {
|
|
|
241
243
|
let sawNonUserInCurrentTurn = false;
|
|
242
244
|
for (let i = 0; i < messages.length; i++) {
|
|
243
245
|
const message = messages[i];
|
|
244
|
-
if (message.role === "system")
|
|
246
|
+
if (message.role === "system" || message.role === "meta")
|
|
245
247
|
continue;
|
|
246
248
|
if (message.role === "user") {
|
|
247
249
|
currentTurnStart = i;
|
|
@@ -157,7 +157,7 @@ async function handleMemoryCommand(args, ctx) {
|
|
|
157
157
|
return lines.join("\n");
|
|
158
158
|
}
|
|
159
159
|
if (sub === "add") {
|
|
160
|
-
return "Manual memory writes are disabled. Bubble now follows the
|
|
160
|
+
return "Manual memory writes are disabled. Bubble now follows the automatic startup memory pipeline.";
|
|
161
161
|
}
|
|
162
162
|
if (sub === "search") {
|
|
163
163
|
const query = rest.join(" ").trim();
|
|
@@ -265,7 +265,7 @@ const builtinSlashCommandEntries = [
|
|
|
265
265
|
},
|
|
266
266
|
{
|
|
267
267
|
name: "memory",
|
|
268
|
-
description: "Inspect and maintain
|
|
268
|
+
description: "Inspect and maintain Bubble's automatic persistent memory. Usage: /memory [status|search|compact|summarize|refresh|reset]",
|
|
269
269
|
async handler(args, ctx) {
|
|
270
270
|
return handleMemoryCommand(args, ctx);
|
|
271
271
|
},
|
|
@@ -737,7 +737,6 @@ const builtinSlashCommandEntries = [
|
|
|
737
737
|
return "Session is already compact enough.";
|
|
738
738
|
}
|
|
739
739
|
const systemMessage = ctx.agent.messages.find((message) => message.role === "system");
|
|
740
|
-
ctx.clearMessages();
|
|
741
740
|
ctx.agent.messages = [
|
|
742
741
|
...(systemMessage ? [systemMessage] : []),
|
|
743
742
|
...ctx.sessionManager.getMessages(),
|
package/dist/system-prompt.d.ts
CHANGED
|
@@ -30,5 +30,7 @@ export interface SystemPromptOptions {
|
|
|
30
30
|
skills?: SkillSummary[];
|
|
31
31
|
/** Prompt-visible memory guidance and summaries */
|
|
32
32
|
memoryPrompt?: string;
|
|
33
|
+
/** Durable child-agent profile prompt used for subagents. */
|
|
34
|
+
agentProfilePrompt?: string;
|
|
33
35
|
}
|
|
34
36
|
export declare function buildSystemPrompt(options?: SystemPromptOptions): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolRegistryEntry } from "../types.js";
|
|
2
|
+
export declare function createSpawnAgentTool(): ToolRegistryEntry;
|
|
3
|
+
export declare function createWaitAgentTool(): ToolRegistryEntry;
|
|
4
|
+
export declare function createSendInputTool(): ToolRegistryEntry;
|
|
5
|
+
export declare function createCloseAgentTool(): ToolRegistryEntry;
|
|
6
|
+
export declare function createAgentLifecycleTools(): ToolRegistryEntry[];
|