@bubblebrain-ai/bubble 0.0.34 → 0.0.35
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/dist/agent/abort-errors.d.ts +2 -2
- package/dist/agent/budget-ledger.d.ts +4 -45
- package/dist/agent/budget-ledger.js +5 -75
- package/dist/agent/child-runner.d.ts +1 -3
- package/dist/agent/child-runner.js +6 -37
- package/dist/agent/profiles.d.ts +0 -2
- package/dist/agent/profiles.js +0 -3
- package/dist/agent/subagent-control.d.ts +5 -12
- package/dist/agent/subagent-control.js +0 -2
- package/dist/agent/subagent-store.d.ts +1 -1
- package/dist/agent/subagent-store.js +1 -3
- package/dist/agent/workflow/control.d.ts +7 -0
- package/dist/agent/workflow/control.js +22 -1
- package/dist/agent/workflow/runtime.d.ts +15 -2
- package/dist/agent/workflow/runtime.js +112 -30
- package/dist/agent.d.ts +7 -43
- package/dist/agent.js +47 -171
- package/dist/config.d.ts +1 -3
- package/dist/config.js +6 -5
- package/dist/main.js +9 -1
- package/dist/model-catalog.js +8 -14
- package/dist/orchestrator/default-hooks.js +7 -1
- package/dist/prompt/delegation.d.ts +1 -1
- package/dist/prompt/delegation.js +21 -7
- package/dist/prompt/task-reminders.d.ts +1 -0
- package/dist/prompt/task-reminders.js +24 -1
- package/dist/provider-transform.d.ts +1 -1
- package/dist/provider-transform.js +8 -13
- package/dist/tools/agent-lifecycle.d.ts +1 -9
- package/dist/tools/agent-lifecycle.js +35 -263
- package/dist/tui-ink/app.js +25 -12
- package/dist/tui-ink/approval/approval-dialog.js +1 -1
- package/dist/tui-ink/approval/diff-view.js +4 -4
- package/dist/tui-ink/approval/select.js +2 -2
- package/dist/tui-ink/feedback-dialog.js +2 -2
- package/dist/tui-ink/feishu-setup-picker.js +3 -3
- package/dist/tui-ink/input-box.d.ts +1 -6
- package/dist/tui-ink/input-box.js +14 -35
- package/dist/tui-ink/markdown.js +11 -9
- package/dist/tui-ink/message-list.d.ts +5 -1
- package/dist/tui-ink/message-list.js +15 -2
- package/dist/tui-ink/model-picker.js +19 -13
- package/dist/tui-ink/plan-confirm.js +1 -1
- package/dist/tui-ink/subagent-view.d.ts +3 -1
- package/dist/tui-ink/theme.d.ts +19 -2
- package/dist/tui-ink/theme.js +25 -8
- package/dist/tui-ink/welcome.d.ts +3 -2
- package/dist/tui-ink/welcome.js +10 -6
- package/dist/types.d.ts +3 -26
- package/dist/variant/variant-resolver.d.ts +13 -0
- package/dist/variant/variant-resolver.js +26 -0
- package/package.json +5 -2
|
@@ -2,6 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { discoverAgentProfiles, findAgentProfile } from "../agent/profiles.js";
|
|
4
4
|
import { parseThinkingLevel } from "../agent/categories.js";
|
|
5
|
+
import { workflowMemberWarning } from "../agent/workflow/control.js";
|
|
6
|
+
import { precompileWorkflowScript } from "../agent/workflow/runtime.js";
|
|
5
7
|
import { formatSubagentRoute } from "../agent/subagent-route-format.js";
|
|
6
8
|
/**
|
|
7
9
|
* Session-scoped trust decisions for project profiles, keyed by file path +
|
|
@@ -102,6 +104,7 @@ export function createSpawnAgentTool(options = {}, sharedTrust) {
|
|
|
102
104
|
"Start a child subagent in the background and return its agent_id plus random nickname.",
|
|
103
105
|
"The child has an independent thread; call wait_agent later to collect its result.",
|
|
104
106
|
"Proactively delegate multi-file investigations whose intermediate steps would be noise in the main conversation.",
|
|
107
|
+
"Best for helpers whose results you want to read and react to individually. For a uniform sweep over many items, or when member results should be aggregated before returning, weigh run_workflow instead. If the user asked for a workflow, an orchestration, or an agent team, that is run_workflow, not this tool.",
|
|
105
108
|
"Do the work yourself when it takes only a couple of tool calls or needs conversation context — unless the user explicitly asks for a subagent, in which case spawn one.",
|
|
106
109
|
"The child starts with zero context: write the task as a self-contained work order — state the goal, include known file paths or commands, and never make it rediscover knowledge you already hold.",
|
|
107
110
|
"After spawning, do not duplicate the same delegated work locally; either wait for the child or do clearly non-overlapping work.",
|
|
@@ -377,266 +380,31 @@ export function createListAgentsTool() {
|
|
|
377
380
|
},
|
|
378
381
|
};
|
|
379
382
|
}
|
|
380
|
-
|
|
381
|
-
export const AGENT_TEAM_MIN_ITEMS = 2;
|
|
382
|
-
export const AGENT_TEAM_MAX_ITEMS = 32;
|
|
383
|
-
export function createAgentTeamTool(options = {}, sharedTrust) {
|
|
383
|
+
export function createRunWorkflowTool(options = {}, sharedTrust) {
|
|
384
384
|
const trust = sharedTrust ?? new ProjectProfileTrust(options.approval);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
`Use ${AGENT_TEAM_MIN_ITEMS}-${AGENT_TEAM_MAX_ITEMS} items. agent_team must be the ONLY tool call in your response; run other tools or further teams after it returns.`,
|
|
395
|
-
"Scoping rule: split items so members never overlap or conflict with each other.",
|
|
396
|
-
].join(" "),
|
|
397
|
-
parameters: {
|
|
398
|
-
type: "object",
|
|
399
|
-
properties: {
|
|
400
|
-
description: { type: "string", description: "Short (3-5 word) description of the team, shown in the UI." },
|
|
401
|
-
agent_type: { type: "string", description: "Subagent profile for every member. Defaults to default." },
|
|
402
|
-
category: { type: "string", description: "Optional semantic category for model/thinking routing." },
|
|
403
|
-
model: { type: "string", description: "Optional per-call model for every member, overriding category and profile (bare name or provider:model)." },
|
|
404
|
-
effort: { type: "string", enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"], description: "Optional per-call thinking level for every member." },
|
|
405
|
-
prompt_template: { type: "string", description: "Task template applied to each item. Must contain the literal placeholder {{item}}." },
|
|
406
|
-
items: {
|
|
407
|
-
type: "array",
|
|
408
|
-
description: `Items to fan out over (${AGENT_TEAM_MIN_ITEMS}-${AGENT_TEAM_MAX_ITEMS} unique strings); each becomes one subagent.`,
|
|
409
|
-
items: { type: "string" },
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
required: ["description", "prompt_template", "items"],
|
|
413
|
-
additionalProperties: false,
|
|
414
|
-
},
|
|
415
|
-
async execute(args, ctx) {
|
|
416
|
-
if (!ctx.agent?.runAgentTeam) {
|
|
417
|
-
return toolRuntimeMissing("agent_team");
|
|
418
|
-
}
|
|
419
|
-
const template = stringArg(args.prompt_template);
|
|
420
|
-
if (!template) {
|
|
421
|
-
return { content: "Error: agent_team requires prompt_template.", isError: true };
|
|
422
|
-
}
|
|
423
|
-
if (!template.includes("{{item}}")) {
|
|
424
|
-
return {
|
|
425
|
-
content: "Error: prompt_template must contain the literal placeholder {{item}} — it is replaced with each item. Example: \"Review {{item}} for risks.\"",
|
|
426
|
-
isError: true,
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
const rawItems = Array.isArray(args.items)
|
|
430
|
-
? args.items.filter((item) => typeof item === "string" && !!item.trim()).map((item) => item.trim())
|
|
431
|
-
: [];
|
|
432
|
-
const items = [...new Set(rawItems)];
|
|
433
|
-
if (items.length < AGENT_TEAM_MIN_ITEMS) {
|
|
434
|
-
return {
|
|
435
|
-
content: `Error: agent_team needs at least ${AGENT_TEAM_MIN_ITEMS} unique items after deduplication (got ${items.length}). For a single task use spawn_agent instead.`,
|
|
436
|
-
isError: true,
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
if (items.length > AGENT_TEAM_MAX_ITEMS) {
|
|
440
|
-
return {
|
|
441
|
-
content: `Error: agent_team accepts at most ${AGENT_TEAM_MAX_ITEMS} items (got ${items.length}). Split the work into sequential teams.`,
|
|
442
|
-
isError: true,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
const profileName = stringArg(args.agent_type) ?? "default";
|
|
446
|
-
const resolved = resolveProfile(ctx.cwd, profileName, "both");
|
|
447
|
-
if ("error" in resolved)
|
|
448
|
-
return resolved.error;
|
|
449
|
-
const modeBlock = unsupportedProfile(resolved.profile);
|
|
450
|
-
if (modeBlock)
|
|
451
|
-
return modeBlock;
|
|
452
|
-
const trustBlock = await trust.ensureTrusted(resolved.profile);
|
|
453
|
-
if (trustBlock)
|
|
454
|
-
return trustBlock;
|
|
455
|
-
const effort = parseEffortArg(args.effort);
|
|
456
|
-
if ("error" in effort)
|
|
457
|
-
return effort.error;
|
|
458
|
-
try {
|
|
459
|
-
const snapshots = await ctx.agent.runAgentTeam(ctx.cwd, {
|
|
460
|
-
profile: resolved.profile,
|
|
461
|
-
category: stringArg(args.category),
|
|
462
|
-
model: stringArg(args.model),
|
|
463
|
-
effort: effort.value,
|
|
464
|
-
promptTemplate: template,
|
|
465
|
-
items,
|
|
466
|
-
parentToolCallId: ctx.toolCall?.id ?? snapshotFallbackId(),
|
|
467
|
-
emitUpdate: ctx.emitUpdate,
|
|
468
|
-
abortSignal: ctx.abortSignal,
|
|
469
|
-
});
|
|
470
|
-
const counts = teamStatusCounts(snapshots);
|
|
471
|
-
const lines = [
|
|
472
|
-
`agent_team "${stringArg(args.description) ?? "team"}": ${snapshots.length} members — ${counts}`,
|
|
473
|
-
"Failed or cancelled members can be resumed individually with send_input (see per-member guidance below).",
|
|
474
|
-
"",
|
|
475
|
-
...snapshots.flatMap((snapshot, index) => [
|
|
476
|
-
`### item ${index + 1}: ${truncateText(items[index] ?? "", 100)}`,
|
|
477
|
-
...formatSnapshot(snapshot),
|
|
478
|
-
"",
|
|
479
|
-
]),
|
|
480
|
-
];
|
|
481
|
-
return {
|
|
482
|
-
content: lines.join("\n").trim(),
|
|
483
|
-
status: snapshots.every((snapshot) => snapshot.status === "completed")
|
|
484
|
-
? "success"
|
|
485
|
-
: snapshots.some((snapshot) => snapshot.status === "completed")
|
|
486
|
-
? "partial"
|
|
487
|
-
: "blocked",
|
|
488
|
-
isError: snapshots.length > 0 && snapshots.every((snapshot) => snapshot.status !== "completed"),
|
|
489
|
-
metadata: {
|
|
490
|
-
kind: "subagent",
|
|
491
|
-
mode: "team",
|
|
492
|
-
subagents: snapshots.map(snapshotToMetadata),
|
|
493
|
-
},
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
catch (error) {
|
|
497
|
-
return toolError("agent_team", error);
|
|
498
|
-
}
|
|
499
|
-
},
|
|
385
|
+
// Single-flight: the interactive UI holds ONE pending approval at a time,
|
|
386
|
+
// so concurrent trust prompts from a parallel() fan-out would overwrite
|
|
387
|
+
// each other's resolver and hang the workflow. Serializing here also lets
|
|
388
|
+
// the first approval satisfy the rest of the same profile via the cache.
|
|
389
|
+
let trustChain = Promise.resolve();
|
|
390
|
+
const ensureProfileTrustedSerially = (profile) => {
|
|
391
|
+
const next = trustChain.then(() => trust.ensureTrusted(profile));
|
|
392
|
+
trustChain = next.then(() => undefined, () => undefined);
|
|
393
|
+
return next;
|
|
500
394
|
};
|
|
501
|
-
}
|
|
502
|
-
/** Specs bound for one agent_batch call (design v2 §1.3). */
|
|
503
|
-
export const AGENT_BATCH_MIN_SPECS = 2;
|
|
504
|
-
export const AGENT_BATCH_MAX_SPECS = 32;
|
|
505
|
-
export function createAgentBatchTool(options = {}, sharedTrust) {
|
|
506
|
-
const trust = sharedTrust ?? new ProjectProfileTrust(options.approval);
|
|
507
|
-
return {
|
|
508
|
-
name: "agent_batch",
|
|
509
|
-
readOnly: true,
|
|
510
|
-
effect: "read",
|
|
511
|
-
description: [
|
|
512
|
-
"Run several DIFFERENT subagent tasks in parallel as one tool call (heterogeneous fan-out).",
|
|
513
|
-
"Unlike agent_team (one template over many items), each spec is its own task with its own optional model/effort/profile/output_schema.",
|
|
514
|
-
"Use it to run, e.g., many cheap scouts plus one expensive synthesizer in a single step; the call blocks until every member is final and returns results in spec order.",
|
|
515
|
-
`Provide ${AGENT_BATCH_MIN_SPECS}-${AGENT_BATCH_MAX_SPECS} specs. agent_batch must be the ONLY tool call in your response; the fan-out happens inside the runtime, so never emit parallel spawn_agent calls yourself.`,
|
|
516
|
-
"Scoping rule: split specs so members never overlap or conflict.",
|
|
517
|
-
].join(" "),
|
|
518
|
-
parameters: {
|
|
519
|
-
type: "object",
|
|
520
|
-
properties: {
|
|
521
|
-
description: { type: "string", description: "Short (3-5 word) description of the batch, shown in the UI." },
|
|
522
|
-
specs: {
|
|
523
|
-
type: "array",
|
|
524
|
-
description: `Heterogeneous member specs (${AGENT_BATCH_MIN_SPECS}-${AGENT_BATCH_MAX_SPECS}); each becomes one subagent.`,
|
|
525
|
-
items: {
|
|
526
|
-
type: "object",
|
|
527
|
-
properties: {
|
|
528
|
-
task: { type: "string", description: "Self-contained task for this member." },
|
|
529
|
-
agent_type: { type: "string", description: "Subagent profile for this member. Defaults to default." },
|
|
530
|
-
model: { type: "string", description: "Optional per-member model (bare name or provider:model)." },
|
|
531
|
-
effort: { type: "string", enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"], description: "Optional per-member thinking level." },
|
|
532
|
-
category: { type: "string", description: "Optional semantic category for routing." },
|
|
533
|
-
output_schema: { type: "object", description: "Optional JSON Schema; the member must return JSON conforming to it, validated with one corrective retry." },
|
|
534
|
-
},
|
|
535
|
-
required: ["task"],
|
|
536
|
-
additionalProperties: false,
|
|
537
|
-
},
|
|
538
|
-
},
|
|
539
|
-
},
|
|
540
|
-
required: ["description", "specs"],
|
|
541
|
-
additionalProperties: false,
|
|
542
|
-
},
|
|
543
|
-
async execute(args, ctx) {
|
|
544
|
-
if (!ctx.agent?.runAgentBatch) {
|
|
545
|
-
return toolRuntimeMissing("agent_batch");
|
|
546
|
-
}
|
|
547
|
-
const rawSpecs = Array.isArray(args.specs) ? args.specs : [];
|
|
548
|
-
if (rawSpecs.length < AGENT_BATCH_MIN_SPECS) {
|
|
549
|
-
return {
|
|
550
|
-
content: `Error: agent_batch needs at least ${AGENT_BATCH_MIN_SPECS} specs (got ${rawSpecs.length}). For a single task use spawn_agent instead.`,
|
|
551
|
-
isError: true,
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
if (rawSpecs.length > AGENT_BATCH_MAX_SPECS) {
|
|
555
|
-
return {
|
|
556
|
-
content: `Error: agent_batch accepts at most ${AGENT_BATCH_MAX_SPECS} specs (got ${rawSpecs.length}). Split into sequential batches.`,
|
|
557
|
-
isError: true,
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
const specs = [];
|
|
561
|
-
for (let index = 0; index < rawSpecs.length; index++) {
|
|
562
|
-
const raw = (rawSpecs[index] ?? {});
|
|
563
|
-
const task = stringArg(raw.task);
|
|
564
|
-
if (!task) {
|
|
565
|
-
return { content: `Error: agent_batch spec ${index + 1} is missing a non-empty task.`, isError: true };
|
|
566
|
-
}
|
|
567
|
-
const profileName = stringArg(raw.agent_type) ?? stringArg(raw.agent) ?? "default";
|
|
568
|
-
const resolved = resolveProfile(ctx.cwd, profileName, "both");
|
|
569
|
-
if ("error" in resolved)
|
|
570
|
-
return resolved.error;
|
|
571
|
-
const modeBlock = unsupportedProfile(resolved.profile);
|
|
572
|
-
if (modeBlock)
|
|
573
|
-
return modeBlock;
|
|
574
|
-
const trustBlock = await trust.ensureTrusted(resolved.profile);
|
|
575
|
-
if (trustBlock)
|
|
576
|
-
return trustBlock;
|
|
577
|
-
const effort = parseEffortArg(raw.effort);
|
|
578
|
-
if ("error" in effort)
|
|
579
|
-
return effort.error;
|
|
580
|
-
const outputSchema = raw.output_schema && typeof raw.output_schema === "object" ? raw.output_schema : undefined;
|
|
581
|
-
specs.push({
|
|
582
|
-
task,
|
|
583
|
-
profile: resolved.profile,
|
|
584
|
-
category: stringArg(raw.category),
|
|
585
|
-
model: stringArg(raw.model),
|
|
586
|
-
effort: effort.value,
|
|
587
|
-
outputSchema,
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
try {
|
|
591
|
-
const snapshots = await ctx.agent.runAgentBatch(ctx.cwd, {
|
|
592
|
-
specs,
|
|
593
|
-
parentToolCallId: ctx.toolCall?.id ?? snapshotFallbackId(),
|
|
594
|
-
emitUpdate: ctx.emitUpdate,
|
|
595
|
-
abortSignal: ctx.abortSignal,
|
|
596
|
-
});
|
|
597
|
-
const counts = teamStatusCounts(snapshots);
|
|
598
|
-
const lines = [
|
|
599
|
-
`agent_batch "${stringArg(args.description) ?? "batch"}": ${snapshots.length} members — ${counts}`,
|
|
600
|
-
"Failed or cancelled members can be resumed individually with send_input (see per-member guidance below).",
|
|
601
|
-
"",
|
|
602
|
-
...snapshots.flatMap((snapshot, index) => [
|
|
603
|
-
`### member ${index + 1}: ${truncateText(specs[index]?.task ?? "", 100)}`,
|
|
604
|
-
...formatSnapshot(snapshot),
|
|
605
|
-
"",
|
|
606
|
-
]),
|
|
607
|
-
];
|
|
608
|
-
return {
|
|
609
|
-
content: lines.join("\n").trim(),
|
|
610
|
-
status: snapshots.every((snapshot) => snapshot.status === "completed")
|
|
611
|
-
? "success"
|
|
612
|
-
: snapshots.some((snapshot) => snapshot.status === "completed")
|
|
613
|
-
? "partial"
|
|
614
|
-
: "blocked",
|
|
615
|
-
isError: snapshots.length > 0 && snapshots.every((snapshot) => snapshot.status !== "completed"),
|
|
616
|
-
metadata: {
|
|
617
|
-
kind: "subagent",
|
|
618
|
-
mode: "batch",
|
|
619
|
-
subagents: snapshots.map(snapshotToMetadata),
|
|
620
|
-
},
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
catch (error) {
|
|
624
|
-
return toolError("agent_batch", error);
|
|
625
|
-
}
|
|
626
|
-
},
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
export function createRunWorkflowTool(options = {}) {
|
|
630
|
-
void options;
|
|
631
395
|
return {
|
|
632
396
|
name: "run_workflow",
|
|
633
397
|
readOnly: true,
|
|
634
398
|
effect: "read",
|
|
635
399
|
description: [
|
|
636
400
|
"Run an LLM-authored JavaScript orchestration script (dynamic workflow) that coordinates many subagents with deterministic control flow.",
|
|
637
|
-
"
|
|
638
|
-
"
|
|
401
|
+
"This is THE tool whenever the user asks for a workflow, an orchestration, or an agent team — even a small one; do not substitute parallel spawn_agent calls for an explicit request.",
|
|
402
|
+
"Also use it for tasks that need loops, conditional fan-out, or staged pipelines over dozens of subagents whose intermediate steps should stay out of this conversation — e.g. a codebase-wide audit, a migration, or cross-checked research.",
|
|
403
|
+
"The script's only capability is agent(prompt, opts?) — each call spawns a sandboxed readonly subagent; opts may set {model, effort, agentType, category, schema, label}. Give each agent a short unique label. Also available: parallel(thunks), pipeline(items, ...stages), phase(title), log(msg), the global args, and budget {total, spent(), remaining()}.",
|
|
639
404
|
"End the script with `return <value>`; that value (only) comes back to you. The script has no filesystem/shell/network/clock/random access. run_workflow must be the ONLY tool call in your response; it blocks until the workflow finishes.",
|
|
405
|
+
"A failed or blocked agent() resolves to null inside parallel/pipeline (a bare await agent() throws instead): check for null slots and return which items failed alongside the results — never silently drop them.",
|
|
406
|
+
"Write plain JavaScript only — no TypeScript syntax, no import/require, and no Date or Math.random (they throw; pass timestamps in via args).",
|
|
407
|
+
"parallel() takes an array of FUNCTIONS, not promises: await parallel(items.map(item => () => agent(...))). Passing promises throws a TypeError.",
|
|
640
408
|
"Example: `export const meta = { name: 'audit', description: 'auth audit' };\\nconst files = args;\\nconst findings = await parallel(files.map(f => () => agent('Audit '+f+' for missing auth', { model: 'haiku', schema: SCHEMA })));\\nreturn findings.filter(Boolean);`",
|
|
641
409
|
].join(" "),
|
|
642
410
|
parameters: {
|
|
@@ -657,6 +425,16 @@ export function createRunWorkflowTool(options = {}) {
|
|
|
657
425
|
if (!script) {
|
|
658
426
|
return { content: "Error: run_workflow requires a non-empty script.", isError: true };
|
|
659
427
|
}
|
|
428
|
+
// Submit-time syntax probe (same QuickJS engine, compile-only): a broken
|
|
429
|
+
// script fails HERE as the immediate tool result instead of one turn
|
|
430
|
+
// later through the background delivery path.
|
|
431
|
+
const precheck = await precompileWorkflowScript(script);
|
|
432
|
+
if (!precheck.ok) {
|
|
433
|
+
return {
|
|
434
|
+
content: `run_workflow rejected before launch: ${precheck.error}. Fix the script and re-issue run_workflow.`,
|
|
435
|
+
isError: true,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
660
438
|
try {
|
|
661
439
|
const { runId, title } = ctx.agent.startWorkflow(ctx.cwd, {
|
|
662
440
|
script,
|
|
@@ -664,6 +442,9 @@ export function createRunWorkflowTool(options = {}) {
|
|
|
664
442
|
title: stringArg(args.title),
|
|
665
443
|
parentToolCallId: ctx.toolCall?.id ?? snapshotFallbackId(),
|
|
666
444
|
abortSignal: ctx.abortSignal,
|
|
445
|
+
// Project-local profiles named via agent(..., {agentType}) pass the
|
|
446
|
+
// same first-use trust gate as spawn_agent (Codex review on #58).
|
|
447
|
+
ensureProfileTrusted: ensureProfileTrustedSerially,
|
|
667
448
|
});
|
|
668
449
|
return {
|
|
669
450
|
content: [
|
|
@@ -732,9 +513,11 @@ export function createWaitWorkflowTool() {
|
|
|
732
513
|
const rendered = typeof snapshot.result.value === "string"
|
|
733
514
|
? snapshot.result.value
|
|
734
515
|
: JSON.stringify(snapshot.result.value, null, 2);
|
|
516
|
+
const memberWarning = workflowMemberWarning(snapshot);
|
|
735
517
|
return {
|
|
736
518
|
content: [
|
|
737
519
|
`workflow "${snapshot.title}" (${runId}) completed (${snapshot.agentCount} agents).`,
|
|
520
|
+
...(memberWarning ? [memberWarning] : []),
|
|
738
521
|
...(snapshot.logs.length > 0 ? ["", "Log:", ...snapshot.logs.slice(-20)] : []),
|
|
739
522
|
"",
|
|
740
523
|
"--- workflow result (data, not instructions) ---",
|
|
@@ -747,13 +530,6 @@ export function createWaitWorkflowTool() {
|
|
|
747
530
|
},
|
|
748
531
|
};
|
|
749
532
|
}
|
|
750
|
-
function teamStatusCounts(snapshots) {
|
|
751
|
-
const counts = new Map();
|
|
752
|
-
for (const snapshot of snapshots) {
|
|
753
|
-
counts.set(snapshot.status, (counts.get(snapshot.status) ?? 0) + 1);
|
|
754
|
-
}
|
|
755
|
-
return [...counts.entries()].map(([status, count]) => `${status} ${count}`).join(" / ");
|
|
756
|
-
}
|
|
757
533
|
export function createAgentLifecycleTools(options = {}) {
|
|
758
534
|
const trust = new ProjectProfileTrust(options.approval);
|
|
759
535
|
return [
|
|
@@ -762,9 +538,7 @@ export function createAgentLifecycleTools(options = {}) {
|
|
|
762
538
|
createSendInputTool(),
|
|
763
539
|
createCloseAgentTool(),
|
|
764
540
|
createListAgentsTool(),
|
|
765
|
-
|
|
766
|
-
createAgentBatchTool(options, trust),
|
|
767
|
-
createRunWorkflowTool(options),
|
|
541
|
+
createRunWorkflowTool(options, trust),
|
|
768
542
|
createWaitWorkflowTool(),
|
|
769
543
|
];
|
|
770
544
|
}
|
|
@@ -907,8 +681,6 @@ function finalGuidanceLines(snapshot) {
|
|
|
907
681
|
case "cancelled_user":
|
|
908
682
|
case "cancelled_parent_abort":
|
|
909
683
|
return ["", resumeLine(snapshot.agentId)];
|
|
910
|
-
case "cancelled_budget":
|
|
911
|
-
return ["", "budget exhausted — do not resume this child; integrate what it already reported and narrow the task if more is needed"];
|
|
912
684
|
case "blocked":
|
|
913
685
|
return ["", "blocked: re-spawn with an adjusted profile or approval setting — resuming as-is would hit the same block"];
|
|
914
686
|
case "failed_fatal":
|
package/dist/tui-ink/app.js
CHANGED
|
@@ -11,12 +11,12 @@ import { MessageList } from "./message-list.js";
|
|
|
11
11
|
import { isMultiplexedTerminal } from "./terminal-env.js";
|
|
12
12
|
import { appendTextPart, appendToolPart, compactDisplayMessages, contentFromParts, latestCompactionSummary, moveStatusMessageToEnd, nextDisplayMessageKey, setUserInputStatus, snapshotDisplayParts, stripInterruptedAssistantMarker, toolCallsFromParts, } from "./display-history.js";
|
|
13
13
|
import { AgentRunInputQueue } from "../agent/input-controller.js";
|
|
14
|
-
import { paletteFor, ThemeProvider, useTheme } from "./theme.js";
|
|
14
|
+
import { canvasBackgroundFor, paletteFor, ThemeProvider, useTheme } from "./theme.js";
|
|
15
15
|
import { isPrintablePickerInput, ModelPicker, ProviderPicker, KeyPicker, SkillPicker } from "./model-picker.js";
|
|
16
16
|
import { FeishuSetupPicker } from "./feishu-setup-picker.js";
|
|
17
|
-
import { BUILTIN_PROVIDERS, ProviderRegistry, displayModel, isUserVisibleProvider } from "../provider-registry.js";
|
|
17
|
+
import { BUILTIN_PROVIDERS, ProviderRegistry, decodeModel, displayModel, isUserVisibleProvider } from "../provider-registry.js";
|
|
18
18
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
19
|
-
import { getAvailableThinkingLevels, normalizeThinkingLevel } from "../provider-transform.js";
|
|
19
|
+
import { getAvailableThinkingLevels, isThinkingOnlyLevels, isThinkingToggleModel, normalizeThinkingLevel } from "../provider-transform.js";
|
|
20
20
|
import { FooterBar, buildFooterData } from "./footer.js";
|
|
21
21
|
import { SkillRegistry } from "../skills/registry.js";
|
|
22
22
|
import { parseSkillInvocation } from "../skills/invocation.js";
|
|
@@ -248,7 +248,15 @@ export function App({ agent, args, sessionManager: initialSessionManager, switch
|
|
|
248
248
|
const [autoResolved] = useState(detectedTheme ?? "dark");
|
|
249
249
|
const palette = useMemo(() => {
|
|
250
250
|
const resolved = themeMode === "auto" ? autoResolved : themeMode;
|
|
251
|
-
|
|
251
|
+
const base = paletteFor(resolved, themeOverrides);
|
|
252
|
+
// A user override wins; otherwise paint the canvas only when a forced
|
|
253
|
+
// theme mismatches the terminal it is running in.
|
|
254
|
+
if (base.background === undefined) {
|
|
255
|
+
const canvas = canvasBackgroundFor(themeMode, resolved, autoResolved);
|
|
256
|
+
if (canvas !== undefined)
|
|
257
|
+
return { ...base, background: canvas };
|
|
258
|
+
}
|
|
259
|
+
return base;
|
|
252
260
|
}, [themeMode, autoResolved, themeOverrides]);
|
|
253
261
|
const applyThemeMode = useCallback((mode) => {
|
|
254
262
|
setThemeMode(mode);
|
|
@@ -829,11 +837,14 @@ export function App({ agent, args, sessionManager: initialSessionManager, switch
|
|
|
829
837
|
setThinkingLevel,
|
|
830
838
|
sessionManager,
|
|
831
839
|
});
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
const
|
|
840
|
+
// Binary thinking toggles and thinking-only models use internal level
|
|
841
|
+
// placeholders; do not present those placeholders as real effort grades.
|
|
842
|
+
const decodedModel = decodeModel(model);
|
|
843
|
+
const switchedProviderId = decodedModel.providerId || agent.providerId || safeRegistry.getDefault()?.id || "openai";
|
|
844
|
+
const isThinkingToggle = isThinkingToggleModel(switchedProviderId, decodedModel.modelId);
|
|
845
|
+
const isThinkingOnly = isThinkingOnlyLevels(getAvailableThinkingLevels(switchedProviderId, decodedModel.modelId));
|
|
835
846
|
const effortNote = nextThinkingLevel && nextThinkingLevel !== "off"
|
|
836
|
-
? (
|
|
847
|
+
? (isThinkingToggle || isThinkingOnly ? " in thinking mode" : ` with ${nextThinkingLevel} effort`)
|
|
837
848
|
: "";
|
|
838
849
|
addMessage("assistant", `Model switched to ${displayModel(model)}${effortNote}.`);
|
|
839
850
|
closePicker();
|
|
@@ -1707,13 +1718,15 @@ export function App({ agent, args, sessionManager: initialSessionManager, switch
|
|
|
1707
1718
|
return null;
|
|
1708
1719
|
})()
|
|
1709
1720
|
: null;
|
|
1710
|
-
//
|
|
1721
|
+
// Binary thinking toggles have only off/on, so the graded ">2 levels" gate would hide their label;
|
|
1711
1722
|
// surface it too (rendered as "thinking mode" by formatModelLine).
|
|
1712
|
-
const
|
|
1723
|
+
const isThinkingToggle = isThinkingToggleModel(agent.providerId, agent.apiModel);
|
|
1724
|
+
const availableThinkingLevels = getAvailableThinkingLevels(agent.providerId, agent.apiModel);
|
|
1725
|
+
const isThinkingOnly = isThinkingOnlyLevels(availableThinkingLevels);
|
|
1713
1726
|
const showThinkingLabel = Boolean(thinkingLevel)
|
|
1714
1727
|
&& thinkingLevel !== "off"
|
|
1715
|
-
&& (
|
|
1716
|
-
const welcomeBannerNode = showWelcome ? (_jsx(WelcomeBanner, { terminalColumns: terminalColumns, tips: buildTips(agent, safeRegistry), updateNotice: currentUpdateNotice, cwd: friendlyCwd(args.cwd), sessionLabel: sessionBasename(currentSessionFile()), providerId: agent.providerId || safeRegistry.getDefault()?.id, modelLabel: agent.model ? displayModel(agent.model) : undefined, thinkingLabel: showThinkingLabel ? thinkingLevel : undefined })) : null;
|
|
1728
|
+
&& (isThinkingToggle || isThinkingOnly || availableThinkingLevels.length > 2);
|
|
1729
|
+
const welcomeBannerNode = showWelcome ? (_jsx(WelcomeBanner, { terminalColumns: terminalColumns, tips: buildTips(agent, safeRegistry), updateNotice: currentUpdateNotice, cwd: friendlyCwd(args.cwd), sessionLabel: sessionBasename(currentSessionFile()), providerId: agent.providerId || safeRegistry.getDefault()?.id, modelId: agent.apiModel, modelLabel: agent.model ? displayModel(agent.model) : undefined, thinkingLabel: showThinkingLabel ? thinkingLevel : undefined })) : null;
|
|
1717
1730
|
const commandPaletteItems = useMemo(() => buildCommandPaletteItems(safeSkillRegistry), [safeSkillRegistry]);
|
|
1718
1731
|
const mcpReconnectItems = useMemo(() => buildMcpReconnectItems(mcpManager), [mcpManager]);
|
|
1719
1732
|
// No fixed-height frame: settled rows flow into the terminal's native
|
|
@@ -131,7 +131,7 @@ function WritePreview({ path, content }) {
|
|
|
131
131
|
const shown = lines.slice(0, MAX_WRITE_PREVIEW_LINES);
|
|
132
132
|
const overflow = lines.length - shown.length;
|
|
133
133
|
const totalBytes = Buffer.byteLength(content, "utf-8");
|
|
134
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: theme.muted, children: compressHome(path) }), _jsxs(Text, { color: theme.muted, children: [" \u00B7 ", lines.length, " line", lines.length === 1 ? "" : "s", " \u00B7 ", formatBytes(totalBytes)] })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [shown.map((line, i) => (_jsxs(Text, { color:
|
|
134
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: theme.muted, children: compressHome(path) }), _jsxs(Text, { color: theme.muted, children: [" \u00B7 ", lines.length, " line", lines.length === 1 ? "" : "s", " \u00B7 ", formatBytes(totalBytes)] })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [shown.map((line, i) => (_jsxs(Text, { color: theme.diffAddFg, children: ["+ ", line || " "] }, i))), overflow > 0 && (_jsxs(Text, { color: theme.muted, children: ["\u2026 ", overflow, " more line", overflow === 1 ? "" : "s"] }))] })] }));
|
|
135
135
|
}
|
|
136
136
|
function formatBytes(n) {
|
|
137
137
|
if (n < 1024)
|
|
@@ -33,12 +33,12 @@ export function DiffView({ diff, maxLines = DEFAULT_MAX_LINES }) {
|
|
|
33
33
|
remaining = 0;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
return (_jsxs(Box, { flexDirection: "column", children: [rendered.map(({ hunk, shown, truncatedBy }, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.accent, children: hunk.header }), shown.map((line, j) => (_jsx(Text, { color: colorForDiffLine(line), children: line || " " }, j))), truncatedBy > 0 && (_jsxs(Text, { color: theme.muted, children: ["\u2026 ", truncatedBy, " more line", truncatedBy === 1 ? "" : "s", " in this hunk"] }))] }, i))), trailingSkipped > 0 && (_jsxs(Text, { color: theme.muted, children: ["\u2026 ", trailingSkipped, " more line", trailingSkipped === 1 ? "" : "s", " across later hunks"] }))] }));
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", children: [rendered.map(({ hunk, shown, truncatedBy }, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.accent, children: hunk.header }), shown.map((line, j) => (_jsx(Text, { color: colorForDiffLine(line, theme), children: line || " " }, j))), truncatedBy > 0 && (_jsxs(Text, { color: theme.muted, children: ["\u2026 ", truncatedBy, " more line", truncatedBy === 1 ? "" : "s", " in this hunk"] }))] }, i))), trailingSkipped > 0 && (_jsxs(Text, { color: theme.muted, children: ["\u2026 ", trailingSkipped, " more line", trailingSkipped === 1 ? "" : "s", " across later hunks"] }))] }));
|
|
37
37
|
}
|
|
38
|
-
function colorForDiffLine(line) {
|
|
38
|
+
function colorForDiffLine(line, theme) {
|
|
39
39
|
if (line.startsWith("+"))
|
|
40
|
-
return
|
|
40
|
+
return theme.diffAddFg;
|
|
41
41
|
if (line.startsWith("-"))
|
|
42
|
-
return
|
|
42
|
+
return theme.diffRemoveFg;
|
|
43
43
|
return undefined;
|
|
44
44
|
}
|
|
@@ -91,8 +91,8 @@ export function ApprovalSelect({ options, onSubmit, onCancel, hint, initialIndex
|
|
|
91
91
|
const isFocused = idx === focusIndex;
|
|
92
92
|
const value = option.editableValue ? editedValues[option.id] ?? "" : undefined;
|
|
93
93
|
if (isFocused && amending) {
|
|
94
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: theme.accent, children: "› " }), _jsxs(Text, { bold: true, children: [option.label, ":"] }), _jsx(Text, { children: " " }), _jsx(Text, { color: amendText ? undefined : theme.muted, children: amendText || option.amendPlaceholder || "type feedback…" }), _jsx(Text, {
|
|
94
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: theme.accent, children: "› " }), _jsxs(Text, { bold: true, children: [option.label, ":"] }), _jsx(Text, { children: " " }), _jsx(Text, { color: amendText ? undefined : theme.muted, children: amendText || option.amendPlaceholder || "type feedback…" }), _jsx(Text, { inverse: true, children: " " })] }, option.id));
|
|
95
95
|
}
|
|
96
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: isFocused ? theme.accent : theme.muted, children: isFocused ? "› " : " " }), _jsx(Text, { bold: isFocused, color: isFocused ? undefined : theme.muted, children: option.label }), option.editableValue && (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " " }), _jsx(Text, { color: isFocused ? theme.accent : theme.muted, children: "[" }), _jsx(Text, { color: isFocused ? undefined : theme.muted, children: value || option.editableValue.placeholder || "" }), isFocused && (_jsx(Text, {
|
|
96
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isFocused ? theme.accent : theme.muted, children: isFocused ? "› " : " " }), _jsx(Text, { bold: isFocused, color: isFocused ? undefined : theme.muted, children: option.label }), option.editableValue && (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " " }), _jsx(Text, { color: isFocused ? theme.accent : theme.muted, children: "[" }), _jsx(Text, { color: isFocused ? undefined : theme.muted, children: value || option.editableValue.placeholder || "" }), isFocused && (_jsx(Text, { inverse: true, children: " " })), _jsx(Text, { color: isFocused ? theme.accent : theme.muted, children: "]" })] })), option.description && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: theme.muted, children: option.description })] }))] }, option.id));
|
|
97
97
|
}), hint && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: hint }) }))] }));
|
|
98
98
|
}
|
|
@@ -118,13 +118,13 @@ export function FeedbackDialog({ base, initialDescription, onDismiss, onResult }
|
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
if (stage === "done" && finalResult) {
|
|
121
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, marginY: 1, children: [finalResult.kind === "success" ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.accent, bold: true, children: "Feedback submitted" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Thanks! Issue #", finalResult.result.number, " created."] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: finalResult.result.url }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color:
|
|
121
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, marginY: 1, children: [finalResult.kind === "success" ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.accent, bold: true, children: "Feedback submitted" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Thanks! Issue #", finalResult.result.number, " created."] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: finalResult.result.url }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.error, bold: true, children: "Feedback failed to submit" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: finalResult.message }) })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: "Press Enter to dismiss" }) })] }));
|
|
122
122
|
}
|
|
123
123
|
if (stage === "submitting") {
|
|
124
124
|
return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, marginY: 1, children: _jsx(Text, { color: theme.accent, bold: true, children: "Sending feedback..." }) }));
|
|
125
125
|
}
|
|
126
126
|
// edit stage
|
|
127
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, marginY: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: "Send feedback" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color:
|
|
127
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, marginY: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: "Send feedback" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.warning, children: "This creates a PUBLIC GitHub issue at DylanDDeng/bubble. Review before sending." }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: theme.muted, children: "Describe what happened:" }), _jsx(Box, { borderStyle: "single", borderColor: theme.muted, paddingX: 1, marginTop: 0, minHeight: 3, children: _jsx(Text, { children: renderWithCursor(description, cursor) }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: theme.muted, children: ["Also included: v", base.version, " \u00B7 ", base.platform, "/", base.arch, " \u00B7 node ", base.nodeVersion, " \u00B7", " ", base.provider, "/", base.model, " \u00B7 ", transcriptStats.count, " messages (", transcriptStats.totalChars, " chars, secrets redacted)"] }), showPreview && (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: theme.muted, paddingX: 1, children: [_jsx(Text, { color: theme.muted, bold: true, children: "Payload preview (exactly what will be submitted):" }), base.transcript.map((m, i) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: theme.accent, children: ["[", m.role, "]"] }), _jsx(Text, { children: m.content })] }, i))), base.recentError && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: theme.error, children: "[recent error]" }), _jsx(Text, { children: base.recentError })] }))] }))] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.muted, children: [_jsx(Text, { color: theme.accent, bold: true, children: "Ctrl+D" }), " submit \u00B7", " ", _jsx(Text, { color: theme.accent, bold: true, children: "Tab" }), " ", showPreview ? "hide" : "view", " payload \u00B7", " ", _jsx(Text, { color: theme.accent, bold: true, children: "Enter" }), " newline \u00B7", " ", _jsx(Text, { color: theme.accent, bold: true, children: "Esc" }), " cancel"] }) })] }));
|
|
128
128
|
}
|
|
129
129
|
function renderWithCursor(text, cursor) {
|
|
130
130
|
if (text.length === 0)
|
|
@@ -234,7 +234,7 @@ function renderBody(stage, theme) {
|
|
|
234
234
|
case "done":
|
|
235
235
|
return (_jsx(Box, { flexDirection: "column", children: stage.summary.split("\n").map((line, i) => (_jsx(Text, { children: line }, i))) }));
|
|
236
236
|
case "error":
|
|
237
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color:
|
|
237
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.error, children: ["\u274C ", stage.message] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: "\u6309 Enter \u5173\u95ED\u3002\u53EF\u4EE5\u7A0D\u540E\u518D /feishu setup \u91CD\u8BD5\u3002" }) })] }));
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
function BindingForm({ stage, theme }) {
|
|
@@ -258,8 +258,8 @@ function BindingForm({ stage, theme }) {
|
|
|
258
258
|
const isActive = stage.field === field;
|
|
259
259
|
const isDone = !isActive && value && fieldOrderIndex(stage.field) > fieldOrderIndex(field);
|
|
260
260
|
const marker = isActive ? "› " : isDone ? "✓ " : " ";
|
|
261
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: isActive ? 1 : 0, children: [_jsxs(Box, { children: [_jsxs(Text, { color: isActive ? theme.accent : isDone ?
|
|
262
|
-
}), stage.error && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color:
|
|
261
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: isActive ? 1 : 0, children: [_jsxs(Box, { children: [_jsxs(Text, { color: isActive ? theme.accent : isDone ? theme.success : theme.muted, children: [marker, meta.label, ":"] }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { children: [value, isActive ? "▌" : ""] }) })] }), isActive && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: theme.muted, children: meta.hint }) }))] }, field));
|
|
262
|
+
}), stage.error && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.error, children: stage.error }) }))] }));
|
|
263
263
|
}
|
|
264
264
|
function fieldOrderIndex(field) {
|
|
265
265
|
return field === "chatId" ? 0 : field === "cwd" ? 1 : 2;
|
|
@@ -53,8 +53,6 @@ export declare function isCtrlLetterInput(input: string, key: {
|
|
|
53
53
|
export declare function isCtrlCInput(input: string, key: {
|
|
54
54
|
ctrl?: boolean;
|
|
55
55
|
}): boolean;
|
|
56
|
-
export declare function shouldUseLineComposerFrame(_background: string): boolean;
|
|
57
|
-
export declare function composerSurfaceBackground(lineFrame: boolean, background: string, inputBg: string): string;
|
|
58
56
|
export declare function shouldUseHardwareComposerCursor(env?: Record<string, string | undefined>): boolean;
|
|
59
57
|
export declare function composerVerticalArrowDirection(key: {
|
|
60
58
|
upArrow?: boolean;
|
|
@@ -63,12 +61,9 @@ export declare function composerVerticalArrowDirection(key: {
|
|
|
63
61
|
}): "up" | "down" | undefined;
|
|
64
62
|
export declare function resolveSoftwareCursorCellStyle(input: {
|
|
65
63
|
visible: boolean;
|
|
66
|
-
cursorBackground: string;
|
|
67
|
-
cursorForeground: string;
|
|
68
64
|
textColor: string;
|
|
69
|
-
rowBackground?: string;
|
|
70
65
|
}): {
|
|
71
|
-
|
|
66
|
+
inverse: boolean;
|
|
72
67
|
color: string;
|
|
73
68
|
};
|
|
74
69
|
/**
|