@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21
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/CHANGELOG.md +292 -0
- package/README.md +8 -11
- package/docs/prefix-cache-architecture.md +14 -0
- package/package.json +22 -24
- package/packages/ask-user/README.md +55 -107
- package/packages/ask-user/ask-ui.ts +306 -732
- package/packages/ask-user/clipboard.ts +50 -0
- package/packages/ask-user/config.ts +74 -78
- package/packages/ask-user/package.json +2 -2
- package/packages/ask-user/questions.ts +151 -0
- package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
- package/packages/ask-user/tools.ts +198 -432
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +38 -43
- package/packages/autocomplete/src/sorting.ts +8 -0
- package/packages/background-tasks/package.json +2 -2
- package/packages/background-tasks/src/config.ts +0 -73
- package/packages/background-tasks/src/index.ts +20 -16
- package/packages/background-tasks/src/registry.ts +1 -171
- package/packages/background-tasks/src/types.ts +0 -32
- package/packages/btw/README.md +13 -56
- package/packages/btw/extensions/btw.ts +403 -1796
- package/packages/btw/package.json +5 -2
- package/packages/btw/skills/btw/SKILL.md +7 -143
- package/packages/compactor/README.md +56 -287
- package/packages/compactor/package.json +5 -9
- package/packages/compactor/skills/compactor/SKILL.md +13 -20
- package/packages/compactor/src/card.ts +90 -0
- package/packages/compactor/src/commands/index.ts +73 -130
- package/packages/compactor/src/compaction/compact-args.ts +5 -3
- package/packages/compactor/src/compaction/filter-noise.ts +1 -1
- package/packages/compactor/src/compaction/hooks.ts +487 -311
- package/packages/compactor/src/compaction/source.ts +178 -0
- package/packages/compactor/src/compaction/summarize.ts +586 -52
- package/packages/compactor/src/config/manager.ts +130 -127
- package/packages/compactor/src/config/schema.ts +32 -48
- package/packages/compactor/src/index.ts +40 -431
- package/packages/compactor/src/info-screen.ts +44 -190
- package/packages/compactor/src/session/recall-blocks.ts +1 -1
- package/packages/compactor/src/stats.ts +76 -0
- package/packages/compactor/src/tools/context-budget.ts +17 -34
- package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
- package/packages/compactor/src/tools/register.ts +29 -269
- package/packages/compactor/src/types.ts +47 -186
- package/packages/core/README.md +25 -0
- package/packages/core/child-agent.ts +1 -0
- package/packages/core/command-echo.ts +63 -0
- package/packages/core/compaction-context.ts +41 -0
- package/packages/core/constants.ts +8 -38
- package/packages/core/events.ts +0 -53
- package/packages/core/fusion-status.ts +4 -0
- package/packages/core/global.d.ts +10 -0
- package/packages/core/harness-messages.ts +392 -0
- package/packages/core/index.ts +14 -0
- package/packages/core/kanboard-status.ts +49 -0
- package/packages/core/long-horizon-owner-status.ts +46 -0
- package/packages/core/model-cache.ts +81 -7
- package/packages/core/package.json +6 -3
- package/packages/core/src/attach/detect.ts +163 -0
- package/packages/core/src/child-agent/child-args.ts +31 -0
- package/packages/core/src/child-agent/delivery.ts +41 -0
- package/packages/core/src/child-agent/index.ts +5 -0
- package/packages/core/src/child-agent/readers.ts +203 -0
- package/packages/core/src/child-agent/runtime.ts +584 -0
- package/packages/core/src/evidence.ts +78 -0
- package/packages/core/src/hints/browser.ts +278 -0
- package/packages/core/src/hints/crab-data.ts +19 -0
- package/packages/core/src/hints/index.ts +865 -0
- package/packages/core/src/hints/lines.ts +607 -0
- package/packages/core/src/hints/store.ts +120 -0
- package/packages/core/src/jev/chatter.ts +24 -0
- package/packages/core/src/jev/settings.ts +177 -14
- package/packages/core/src/package-colors.ts +2 -0
- package/packages/core/src/settings/catalog.ts +39 -15
- package/packages/core/src/settings/engine.ts +48 -2
- package/packages/core/src/settings/hub.ts +83 -20
- package/packages/core/src/settings/open.ts +49 -0
- package/packages/core/src/settings/schema.ts +27 -7
- package/packages/core/src/tui/hub-kit.ts +18 -5
- package/packages/core/src/tui/kit.ts +198 -0
- package/packages/core/src/tui/progress.ts +73 -0
- package/packages/core/src/turn/arbiter.ts +290 -0
- package/packages/footer/README.md +4 -6
- package/packages/footer/package.json +3 -3
- package/packages/footer/src/glance-editor.ts +19 -11
- package/packages/footer/src/help.ts +1 -1
- package/packages/footer/src/index.ts +36 -3
- package/packages/footer/src/presets.ts +3 -4
- package/packages/footer/src/rendering/icons.ts +0 -20
- package/packages/footer/src/segments/compactor.ts +52 -142
- package/packages/footer/src/segments/status-ext.ts +0 -1
- package/packages/fusion/README.md +14 -4
- package/packages/fusion/package.json +4 -3
- package/packages/fusion/src/child-args.ts +1 -0
- package/packages/fusion/src/index.ts +161 -8
- package/packages/fusion/src/pi-settings.ts +57 -0
- package/packages/fusion/src/picker.ts +186 -51
- package/packages/fusion/src/preset.ts +56 -2
- package/packages/fusion/src/prompts.ts +1 -1
- package/packages/fusion/src/sidekick-runtime.ts +1 -420
- package/packages/fusion/src/tools.ts +107 -128
- package/packages/fusion/src/transcript.ts +27 -85
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/README.md +18 -30
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/burst.ts +88 -0
- package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
- package/packages/input-shortcuts/src/cleared-input.ts +97 -0
- package/packages/input-shortcuts/src/index.ts +99 -193
- package/packages/input-shortcuts/src/last-response.ts +40 -0
- package/packages/input-shortcuts/src/registers.ts +8 -23
- package/packages/input-shortcuts/src/types.ts +4 -16
- package/packages/input-shortcuts/src/undo-redo.ts +59 -47
- package/packages/kanboard/README.md +51 -47
- package/packages/kanboard/index.ts +293 -55
- package/packages/kanboard/package.json +8 -8
- package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
- package/packages/kanboard/src/badges.ts +93 -0
- package/packages/kanboard/src/bin.ts +4 -2
- package/packages/kanboard/src/commands.ts +218 -84
- package/packages/kanboard/src/debug.ts +20 -0
- package/packages/kanboard/src/guard.ts +267 -109
- package/packages/kanboard/src/monitor.ts +330 -0
- package/packages/kanboard/src/notice-buffer.ts +74 -0
- package/packages/kanboard/src/progress.ts +27 -0
- package/packages/kanboard/src/reminders.ts +234 -0
- package/packages/kanboard/src/settings.ts +69 -15
- package/packages/kanboard/src/shapes.ts +5 -0
- package/packages/long-horizon/index.ts +190 -13
- package/packages/long-horizon/package.json +3 -3
- package/packages/long-horizon/src/commands.ts +103 -7
- package/packages/long-horizon/src/compaction-brief.ts +35 -0
- package/packages/long-horizon/src/engine/continuation.ts +84 -15
- package/packages/long-horizon/src/engine/goal-state.ts +64 -13
- package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
- package/packages/long-horizon/src/engine/ralph.ts +3 -2
- package/packages/long-horizon/src/engine/runaway.ts +68 -11
- package/packages/long-horizon/src/engine/verifier.ts +19 -0
- package/packages/long-horizon/src/gate.ts +37 -12
- package/packages/long-horizon/src/judge/resolve.ts +1 -0
- package/packages/long-horizon/src/owner.ts +35 -2
- package/packages/long-horizon/src/progress.ts +87 -0
- package/packages/long-horizon/src/prompts/goal.ts +1 -1
- package/packages/long-horizon/src/runtime.ts +172 -18
- package/packages/long-horizon/src/settings.ts +54 -39
- package/packages/long-horizon/src/tools/goal.ts +6 -0
- package/packages/long-horizon/src/tools/graph.ts +1 -1
- package/packages/long-horizon/src/tools/swarm.ts +3 -3
- package/packages/mcp/package.json +2 -2
- package/packages/memory/commands.ts +7 -2
- package/packages/memory/index.ts +210 -32
- package/packages/memory/package.json +3 -3
- package/packages/memory/save-session.ts +264 -0
- package/packages/memory/settings.ts +17 -0
- package/packages/memory/tools.ts +253 -203
- package/packages/notify/package.json +2 -2
- package/packages/skill-registry/README.md +53 -16
- package/packages/skill-registry/index.ts +230 -0
- package/packages/skill-registry/package.json +19 -8
- package/packages/skill-registry/src/editor.ts +206 -0
- package/packages/skill-registry/src/judge.ts +289 -0
- package/packages/skill-registry/src/registry.ts +87 -0
- package/packages/skill-registry/src/settings.ts +259 -0
- package/packages/skill-registry/src/vault.ts +80 -0
- package/packages/subagents/README.md +39 -119
- package/packages/subagents/package.json +6 -13
- package/packages/subagents/skills/subagents/SKILL.md +18 -42
- package/packages/subagents/src/agents.ts +213 -0
- package/packages/subagents/src/cards.ts +182 -0
- package/packages/subagents/src/index.ts +609 -1084
- package/packages/subagents/src/manager.ts +429 -0
- package/packages/subagents/src/profiles.ts +130 -0
- package/packages/subagents/src/transcript.ts +154 -0
- package/packages/subagents/src/ui.ts +389 -0
- package/packages/unipi/bundled.js +39756 -45569
- package/packages/unipi/index.ts +55 -10
- package/packages/updater/package.json +2 -2
- package/packages/utility/README.md +76 -69
- package/packages/utility/package.json +2 -2
- package/packages/utility/skills/image/SKILL.md +37 -0
- package/packages/utility/skills/utility/SKILL.md +17 -60
- package/packages/utility/src/answer/extract.ts +128 -0
- package/packages/utility/src/answer/index.ts +213 -0
- package/packages/utility/src/answer/reply.ts +133 -0
- package/packages/utility/src/answer/web.ts +167 -0
- package/packages/utility/src/attach/index.ts +205 -0
- package/packages/utility/src/commands.ts +38 -142
- package/packages/utility/src/diagnostics/engine.ts +50 -5
- package/packages/utility/src/image/index.ts +33 -0
- package/packages/utility/src/image/models.ts +221 -0
- package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
- package/packages/utility/src/image/settings.ts +178 -0
- package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
- package/packages/utility/src/image/tools.ts +168 -0
- package/packages/utility/src/image/transports.ts +186 -0
- package/packages/utility/src/index.ts +68 -389
- package/packages/utility/src/lifecycle/cleanup.ts +116 -311
- package/packages/utility/src/rename/gate.ts +102 -0
- package/packages/utility/src/rename/index.ts +191 -0
- package/packages/utility/src/rename/session.ts +170 -0
- package/packages/utility/src/render/delegated.ts +285 -0
- package/packages/utility/src/render/harness.ts +683 -0
- package/packages/utility/src/render/parse.ts +156 -0
- package/packages/utility/src/render/pi-settings.ts +38 -0
- package/packages/utility/src/render/reply-bg.ts +307 -0
- package/packages/utility/src/render/simple.ts +724 -0
- package/packages/utility/src/render/spacing.ts +235 -0
- package/packages/utility/src/render/styled.ts +251 -0
- package/packages/utility/src/render/tools.ts +353 -0
- package/packages/utility/src/settings.ts +166 -233
- package/packages/utility/src/types.ts +1 -222
- package/packages/watchdog/index.ts +17 -6
- package/packages/watchdog/package.json +3 -3
- package/packages/watchdog/src/config.ts +3 -2
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/README.md +3 -0
- package/packages/workflow/index.ts +0 -2
- package/packages/workflow/package.json +2 -2
- package/packages/workflow/src/permission/decide.ts +6 -4
- package/packages/workflow/src/permission/index.ts +17 -7
- package/packages/workflow/src/permission/settings.ts +39 -4
- package/packages/workflow/src/plan/index.ts +19 -4
- package/packages/background-tasks/extensions/delegate-child.ts +0 -1
- package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
- package/packages/background-tasks/src/context-token-budget.ts +0 -903
- package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
- package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
- package/packages/background-tasks/src/delegate/budget.ts +0 -415
- package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
- package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
- package/packages/background-tasks/src/delegate/launch.ts +0 -484
- package/packages/background-tasks/src/delegate/result-package.ts +0 -459
- package/packages/background-tasks/src/delegate/runner.ts +0 -430
- package/packages/background-tasks/src/delegate/seed.ts +0 -423
- package/packages/background-tasks/src/delegate/types.ts +0 -323
- package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
- package/packages/background-tasks/src/delegate-extension.ts +0 -654
- package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
- package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
- package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
- package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
- package/packages/compactor/src/compaction/build-sections.ts +0 -76
- package/packages/compactor/src/compaction/extract/goals.ts +0 -36
- package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
- package/packages/compactor/src/compaction/format.ts +0 -53
- package/packages/compactor/src/compaction/merge.ts +0 -143
- package/packages/compactor/src/config/presets.ts +0 -160
- package/packages/compactor/src/executor/executor.ts +0 -233
- package/packages/compactor/src/executor/runtime.ts +0 -115
- package/packages/compactor/src/security/evaluator.ts +0 -89
- package/packages/compactor/src/security/policy.ts +0 -96
- package/packages/compactor/src/security/scanner.ts +0 -65
- package/packages/compactor/src/session/auto-inject.ts +0 -60
- package/packages/compactor/src/session/db.ts +0 -325
- package/packages/compactor/src/session/extract.ts +0 -144
- package/packages/compactor/src/session/resume-inject.ts +0 -64
- package/packages/compactor/src/session/snapshot.ts +0 -327
- package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
- package/packages/compactor/src/tools/ctx-execute.ts +0 -23
- package/packages/compactor/src/tools/ctx-stats.ts +0 -96
- package/packages/image/README.md +0 -99
- package/packages/image/package.json +0 -61
- package/packages/image/skills/image/SKILL.md +0 -76
- package/packages/image/src/generate.ts +0 -256
- package/packages/image/src/index.ts +0 -135
- package/packages/image/src/models.ts +0 -519
- package/packages/image/src/openai-images-api.ts +0 -282
- package/packages/image/src/register-providers.ts +0 -209
- package/packages/image/src/settings.ts +0 -173
- package/packages/image/src/tools.ts +0 -326
- package/packages/input-shortcuts/src/clipboard.ts +0 -119
- package/packages/kanboard/src/runner.ts +0 -817
- package/packages/skill-registry/skills/auto/SKILL.md +0 -465
- package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
- package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
- package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
- package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
- package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
- package/packages/skill-registry/skills/debug/SKILL.md +0 -228
- package/packages/skill-registry/skills/document/SKILL.md +0 -120
- package/packages/skill-registry/skills/fix/SKILL.md +0 -212
- package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
- package/packages/skill-registry/skills/plan/SKILL.md +0 -231
- package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
- package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
- package/packages/skill-registry/skills/research/SKILL.md +0 -251
- package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
- package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
- package/packages/skill-registry/skills/work/SKILL.md +0 -218
- package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
- package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
- package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
- package/packages/subagents/agents/delegate.md +0 -14
- package/packages/subagents/agents/oracle.md +0 -78
- package/packages/subagents/agents/researcher.md +0 -52
- package/packages/subagents/agents/reviewer.md +0 -80
- package/packages/subagents/agents/scout.md +0 -50
- package/packages/subagents/agents/worker.md +0 -59
- package/packages/subagents/prompts/council.md +0 -48
- package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
- package/packages/subagents/prompts/parallel-cleanup.md +0 -59
- package/packages/subagents/prompts/parallel-research.md +0 -50
- package/packages/subagents/prompts/parallel-review.md +0 -54
- package/packages/subagents/prompts/review-loop.md +0 -43
- package/packages/subagents/skills/council-mode/SKILL.md +0 -230
- package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
- package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
- package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
- package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
- package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
- package/packages/subagents/src/acceptance.ts +0 -331
- package/packages/subagents/src/agent-manager.ts +0 -405
- package/packages/subagents/src/agent-memory.ts +0 -300
- package/packages/subagents/src/agent-overrides.ts +0 -183
- package/packages/subagents/src/agent-runner.ts +0 -271
- package/packages/subagents/src/async-runner.ts +0 -422
- package/packages/subagents/src/authority-policy.ts +0 -47
- package/packages/subagents/src/budgets.ts +0 -285
- package/packages/subagents/src/child-safety.ts +0 -140
- package/packages/subagents/src/config.ts +0 -249
- package/packages/subagents/src/conversation-viewer.ts +0 -281
- package/packages/subagents/src/core-compat.ts +0 -94
- package/packages/subagents/src/custom-agents.ts +0 -343
- package/packages/subagents/src/file-system-retry.ts +0 -50
- package/packages/subagents/src/fleet-data.ts +0 -105
- package/packages/subagents/src/fleet-view.ts +0 -322
- package/packages/subagents/src/foreground-detach.ts +0 -59
- package/packages/subagents/src/fork-context.ts +0 -285
- package/packages/subagents/src/global.d.ts +0 -7
- package/packages/subagents/src/guide.ts +0 -129
- package/packages/subagents/src/mission-state.ts +0 -133
- package/packages/subagents/src/mission-store.ts +0 -436
- package/packages/subagents/src/output-limits.ts +0 -142
- package/packages/subagents/src/parity-types.ts +0 -315
- package/packages/subagents/src/pi-args.ts +0 -167
- package/packages/subagents/src/result-files.ts +0 -232
- package/packages/subagents/src/result-watcher.ts +0 -221
- package/packages/subagents/src/retained-children.ts +0 -176
- package/packages/subagents/src/run-fanout-budget.ts +0 -356
- package/packages/subagents/src/scheduled-runs.ts +0 -349
- package/packages/subagents/src/schemas.ts +0 -263
- package/packages/subagents/src/slash-commands.ts +0 -80
- package/packages/subagents/src/supervisor-channel.ts +0 -249
- package/packages/subagents/src/tool-handler.ts +0 -1144
- package/packages/subagents/src/types.ts +0 -238
- package/packages/subagents/src/widget.ts +0 -457
- package/packages/subagents/src/workflow-script.ts +0 -737
- package/packages/subagents/src/workflow-worker.ts +0 -384
- package/packages/subagents/src/worktree.ts +0 -614
- package/packages/utility/src/analytics/collector.ts +0 -293
- package/packages/utility/src/info-screen.ts +0 -80
- package/packages/utility/src/lifecycle/process.ts +0 -162
- package/packages/utility/src/prefix-cache.ts +0 -263
- package/packages/utility/src/skill-discovery.ts +0 -542
- package/packages/utility/src/tools/env.ts +0 -113
- package/packages/utility/src/tui/name-badge-state.ts +0 -315
- package/packages/utility/src/tui/name-badge.ts +0 -109
- /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Text, type Component } from "@earendil-works/pi-tui";
|
|
2
2
|
import { type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
+
import { isChildProcess, registerWaitSource } from "@pi-unipi/core";
|
|
4
5
|
import type { SidekickRuntime, HandoffProgress, HandoffReport } from "./sidekick-runtime.js";
|
|
5
|
-
import {
|
|
6
|
+
import { createCompletionDelivery, registerSubagentReader, type SubagentReader } from "@pi-unipi/core/child-agent.js";
|
|
7
|
+
import { duration } from "./transcript.js";
|
|
6
8
|
|
|
7
9
|
const SidekickParams = Type.Object({
|
|
8
10
|
message: Type.String({ description: "A concrete implementation or verification brief for the sidekick" }),
|
|
@@ -22,46 +24,6 @@ export interface FusionToolDeps {
|
|
|
22
24
|
onDetach?: (ctx: ExtensionContext) => void;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
/**
|
|
26
|
-
* Exactly-once completion delivery for handoffs nobody is waiting on.
|
|
27
|
-
*
|
|
28
|
-
* `attach`/`detach` bracket every waiting period. The completion is sent only
|
|
29
|
-
* if the report lands (or has already landed) while no waiter is attached, and
|
|
30
|
-
* only once per handoff id.
|
|
31
|
-
*/
|
|
32
|
-
export function createCompletionDelivery(send: (report: HandoffReport) => void): {
|
|
33
|
-
attach(id: string): void;
|
|
34
|
-
detach(id: string, done: Promise<HandoffReport>): void;
|
|
35
|
-
consume(id: string): void;
|
|
36
|
-
} {
|
|
37
|
-
const waiting = new Set<string>();
|
|
38
|
-
const armed = new Set<string>();
|
|
39
|
-
const delivered = new Set<string>();
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
attach(id) {
|
|
43
|
-
waiting.add(id);
|
|
44
|
-
},
|
|
45
|
-
detach(id, done) {
|
|
46
|
-
waiting.delete(id);
|
|
47
|
-
// One continuation per handoff, however many times a waiter gives up.
|
|
48
|
-
if (armed.has(id)) return;
|
|
49
|
-
armed.add(id);
|
|
50
|
-
void done
|
|
51
|
-
.then((report) => {
|
|
52
|
-
if (waiting.has(id) || delivered.has(id)) return;
|
|
53
|
-
delivered.add(id);
|
|
54
|
-
send(report);
|
|
55
|
-
})
|
|
56
|
-
.catch(() => undefined);
|
|
57
|
-
},
|
|
58
|
-
consume(id) {
|
|
59
|
-
waiting.delete(id);
|
|
60
|
-
delivered.add(id);
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
27
|
function firstLine(value: string): string {
|
|
66
28
|
return value.split("\n", 1)[0] ?? "";
|
|
67
29
|
}
|
|
@@ -141,95 +103,86 @@ type ThemeLike = {
|
|
|
141
103
|
bold: (text: string) => string;
|
|
142
104
|
};
|
|
143
105
|
|
|
144
|
-
type FramedTheme = ThemeLike & { bg: (color: string, text: string) => string };
|
|
145
|
-
|
|
146
|
-
function transcriptStatus(partial: boolean, report: HandoffReport | undefined): "working" | "completed" | "error" {
|
|
147
|
-
if (partial) return "working";
|
|
148
|
-
return report?.status === "completed" ? "completed" : "error";
|
|
149
|
-
}
|
|
150
|
-
|
|
151
106
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
107
|
+
* In-flight NON-blocking handoffs, for the turn arbiter's wait source: a
|
|
108
|
+
* block:false sidekick is still working while the lead settles, so the
|
|
109
|
+
* arbiter must defer (its completion lands as an event turn). Blocking
|
|
110
|
+
* handoffs never reach settle (the lead waits inside the tool call), and
|
|
111
|
+
* interrupted waits detach to background the same way.
|
|
154
112
|
*/
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
113
|
+
export class BackgroundHandoffTracker {
|
|
114
|
+
private readonly handoffs = new Map<string, Promise<HandoffReport>>();
|
|
158
115
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return text
|
|
165
|
-
.replace(/<\/?subagent_completion_notification[^>]*>/g, "")
|
|
166
|
-
.replace(/use `?read_subagent`?[^.\n]*\.?/gi, "")
|
|
167
|
-
.replace(HANDOFF_ID, "")
|
|
168
|
-
.replace(/agent_id[ =]?"?"?/g, "")
|
|
169
|
-
.replace(/read_subagent\([^)]*\)/g, "read_subagent")
|
|
170
|
-
.replace(/\(\s*\)/g, "")
|
|
171
|
-
.replace(/[ \t]{2,}/g, " ")
|
|
172
|
-
.replace(/\bHandoff\s*(?=(is|was|failed|aborted|started)\b)/g, "The handoff ")
|
|
173
|
-
.replace(/ for\s*\./g, ".")
|
|
174
|
-
.replace(/ +$/gm, "")
|
|
175
|
-
.trim();
|
|
176
|
-
}
|
|
116
|
+
/** Track a detached handoff. Cleanup queues behind the completion delivery: every caller detaches the delivery BEFORE tracking, so the wait source only clears after the completion followUp is queued. */
|
|
117
|
+
track(id: string, done: Promise<HandoffReport>): void {
|
|
118
|
+
void done.catch(() => undefined).then(() => this.handoffs.delete(id));
|
|
119
|
+
this.handoffs.set(id, done);
|
|
120
|
+
}
|
|
177
121
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
122
|
+
get size(): number {
|
|
123
|
+
return this.handoffs.size;
|
|
124
|
+
}
|
|
182
125
|
|
|
183
|
-
|
|
184
|
-
|
|
126
|
+
/** Arbiter wait-source reason, or null when nothing is in flight. */
|
|
127
|
+
reason(): string | null {
|
|
128
|
+
return this.handoffs.size > 0 ? "sidekick working" : null;
|
|
129
|
+
}
|
|
185
130
|
}
|
|
186
131
|
|
|
187
|
-
type ToolDetails = Partial<HandoffReport> & { progress?: HandoffProgress; background?: boolean; id?: string };
|
|
188
132
|
|
|
189
|
-
|
|
190
|
-
const status = report?.status ?? "done";
|
|
191
|
-
return `${theme.fg(status === "completed" ? "success" : "error", "◆")} ${theme.fg("accent", theme.bold(`${label} ${status}`))} ${theme.fg("dim", report ? `· ${String(report.toolCalls)} tool calls · ${duration(report.durationMs)} · in ${String(report.usage.input)} / out ${String(report.usage.output)} tokens` : "")}`;
|
|
192
|
-
}
|
|
133
|
+
type ToolDetails = Partial<HandoffReport> & { progress?: HandoffProgress; background?: boolean; id?: string };
|
|
193
134
|
|
|
194
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Compact one-line tool status — the steps themselves stream as
|
|
137
|
+
* `sidekick-step` entries, so the tool card is just the status line:
|
|
138
|
+
* `◆ sidekick working · N steps · 12s` → `◆ sidekick done · N steps · 34s ·
|
|
139
|
+
* in X / out Y tokens`.
|
|
140
|
+
*/
|
|
141
|
+
function renderToolStatus(result: { content?: unknown; details?: unknown; isError?: boolean }, theme: ThemeLike): Component {
|
|
195
142
|
const details = result.details as ToolDetails | undefined;
|
|
196
|
-
if (details?.background === true)
|
|
197
|
-
|
|
143
|
+
if (details?.background === true) {
|
|
144
|
+
return new Text(`${theme.fg("accent", "◆")} ${theme.fg("toolTitle", theme.bold("sidekick"))} ${theme.fg("dim", "· continuing in background")}`, 0, 0);
|
|
145
|
+
}
|
|
198
146
|
const progress = details?.progress;
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
renderText: markdownText,
|
|
213
|
-
}));
|
|
147
|
+
if (progress !== undefined) {
|
|
148
|
+
const steps = progress.toolCalls;
|
|
149
|
+
const elapsed = duration(Date.now() - progress.startedAt);
|
|
150
|
+
return new Text(`${theme.fg("accent", "◆")} ${theme.fg("toolTitle", theme.bold("sidekick"))} ${theme.fg("dim", `working · ${String(steps)} steps · ${elapsed}`)}`, 0, 0);
|
|
151
|
+
}
|
|
152
|
+
const report = details as HandoffReport | undefined;
|
|
153
|
+
const failed = result.isError === true || (report !== undefined && report.status !== "completed" && report.status !== undefined);
|
|
154
|
+
const glyph = failed ? theme.fg("error", "✗") : theme.fg("accent", "◆");
|
|
155
|
+
const label = failed ? "sidekick failed" : "sidekick done";
|
|
156
|
+
const meta = report?.durationMs !== undefined
|
|
157
|
+
? ` · ${String(report.toolCalls)} steps · ${duration(report.durationMs)} · in ${String(report.usage?.input ?? 0)} / out ${String(report.usage?.output ?? 0)} tokens`
|
|
158
|
+
: "";
|
|
159
|
+
return new Text(`${glyph} ${theme.fg("toolTitle", theme.bold(label))}${theme.fg("dim", meta)}`, 0, 0);
|
|
214
160
|
}
|
|
215
161
|
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
isPartial: false,
|
|
223
|
-
report,
|
|
224
|
-
renderText: markdownText,
|
|
225
|
-
}));
|
|
162
|
+
function renderCompletionLine(theme: ThemeLike, report: HandoffReport | undefined): Component {
|
|
163
|
+
const failed = report !== undefined && report.status !== "completed";
|
|
164
|
+
const glyph = failed ? theme.fg("error", "✗") : theme.fg("accent", "◆");
|
|
165
|
+
const label = failed ? "sidekick failed" : "sidekick finished";
|
|
166
|
+
const meta = report?.durationMs !== undefined ? ` · ${String(report.toolCalls)} steps · ${duration(report.durationMs)}` : "";
|
|
167
|
+
return new Text(`${glyph} ${theme.fg("dim", `${label}${meta}`)}`, 0, 0);
|
|
226
168
|
}
|
|
227
169
|
|
|
228
170
|
export function registerFusionTools(pi: ExtensionAPI, deps: FusionToolDeps): void {
|
|
229
|
-
|
|
171
|
+
// Turn-arbiter wait source (lead only).
|
|
172
|
+
const backgroundHandoffs = new BackgroundHandoffTracker();
|
|
173
|
+
const trackBackgroundHandoff = (id: string, done: Promise<HandoffReport>): void => backgroundHandoffs.track(id, done);
|
|
174
|
+
if (!isChildProcess()) {
|
|
175
|
+
try {
|
|
176
|
+
registerWaitSource("fusion", () => backgroundHandoffs.reason());
|
|
177
|
+
} catch {
|
|
178
|
+
// Registration must never block tool registration.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
pi.registerMessageRenderer("sidekick-completion", (message: { details?: HandoffReport }, _options, theme) => renderCompletionLine(theme as unknown as ThemeLike, message.details));
|
|
230
183
|
|
|
231
184
|
// One delivery mechanism for every handoff nobody is waiting on.
|
|
232
|
-
const completion = createCompletionDelivery((report) => {
|
|
185
|
+
const completion = createCompletionDelivery<HandoffReport>((report) => {
|
|
233
186
|
pi.sendMessage(completionMessage(report) as never, { deliverAs: "followUp", triggerTurn: true } as never);
|
|
234
187
|
});
|
|
235
188
|
|
|
@@ -239,7 +192,7 @@ export function registerFusionTools(pi: ExtensionAPI, deps: FusionToolDeps): voi
|
|
|
239
192
|
description: "Hand off work to your persistent sidekick subagent (one per session; context and shells persist across handoffs; runs on the same machine). block:true (default) waits and returns the report. block:false returns immediately and the report arrives later as a <subagent_completion_notification>. Calling again while a handoff is running injects the message as an interrupt rather than starting a second sidekick.",
|
|
240
193
|
parameters: SidekickParams,
|
|
241
194
|
renderCall: (args, theme) => new Text(`${theme.fg("toolTitle", theme.bold("◆ sidekick"))} ${theme.fg("dim", firstLine(String(args.message)).slice(0, 100))}`, 0, 0),
|
|
242
|
-
renderResult: (result,
|
|
195
|
+
renderResult: (result, _options, theme) => renderToolStatus(result, theme as unknown as ThemeLike),
|
|
243
196
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
244
197
|
const runtime = deps.getRuntime(ctx);
|
|
245
198
|
if (!runtime) return result("Fusion is not active — pick a Fusion pair with /unipi:model.", undefined, true);
|
|
@@ -249,18 +202,22 @@ export function registerFusionTools(pi: ExtensionAPI, deps: FusionToolDeps): voi
|
|
|
249
202
|
if (params.block === false) {
|
|
250
203
|
void handoff.done.then((report) => deps.onReport?.(ctx, report)).catch(() => undefined);
|
|
251
204
|
completion.detach(handoff.id, handoff.done);
|
|
205
|
+
trackBackgroundHandoff(handoff.id, handoff.done);
|
|
252
206
|
deps.onDetach?.(ctx);
|
|
253
207
|
return result(`Handoff ${handoff.id} started in the background. You will receive a <subagent_completion_notification agent_id="${handoff.id}"> when it finishes; use read_subagent to wait.`, { background: true, id: handoff.id });
|
|
254
208
|
}
|
|
255
209
|
deps.onAttach?.(ctx);
|
|
210
|
+
runtime.attachUi?.(ctx.ui as never);
|
|
256
211
|
completion.attach(handoff.id);
|
|
257
212
|
const waited = await waitForReport(runtime, handoff.id, handoff.done, signal, ctx, onUpdate ? (update) => onUpdate(update as never) : undefined);
|
|
213
|
+
runtime.detachUi?.();
|
|
258
214
|
if (waited.report) {
|
|
259
215
|
completion.consume(handoff.id);
|
|
260
216
|
deps.onReport?.(ctx, waited.report);
|
|
261
217
|
return result(reportText(waited.report), waited.report, waited.report.status !== "completed");
|
|
262
218
|
}
|
|
263
219
|
completion.detach(handoff.id, handoff.done);
|
|
220
|
+
trackBackgroundHandoff(handoff.id, handoff.done);
|
|
264
221
|
deps.onDetach?.(ctx);
|
|
265
222
|
if (waited.error) return result(`Handoff ${handoff.id} failed: ${waited.error}`, undefined, true);
|
|
266
223
|
if (waited.aborted) return result(`${progressText(runtime, handoff.id)}\nHandoff ${handoff.id} aborted.`, undefined, true);
|
|
@@ -269,14 +226,19 @@ export function registerFusionTools(pi: ExtensionAPI, deps: FusionToolDeps): voi
|
|
|
269
226
|
},
|
|
270
227
|
});
|
|
271
228
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
229
|
+
// Fusion registers a READER for the shared `read_subagent` tool (core).
|
|
230
|
+
// Same semantics as before: consume completions, block:true waits with the
|
|
231
|
+
// shared timeout cap, interrupted waits detach to background.
|
|
232
|
+
const reader: SubagentReader = {
|
|
233
|
+
owns: (id, ctx) => runtime_reports_owns(deps, id, ctx ?? lastCtx),
|
|
234
|
+
latest: (ctx) => {
|
|
235
|
+
const ctx0 = ctx ?? lastCtx;
|
|
236
|
+
const rt = ctx0 === undefined ? undefined : deps.getRuntime(ctx0);
|
|
237
|
+
const latest = rt?.latest();
|
|
238
|
+
if (!latest || !rt) return undefined;
|
|
239
|
+
return { id: latest.id, startedAt: rt.progress(latest.id)?.startedAt ?? 0 };
|
|
240
|
+
},
|
|
241
|
+
read: async (params, signal, onUpdate, ctx) => {
|
|
280
242
|
const runtime = deps.getRuntime(ctx);
|
|
281
243
|
if (!runtime) return result("Fusion is not active — pick a Fusion pair with /unipi:model.", undefined, true);
|
|
282
244
|
const latest = runtime.latest();
|
|
@@ -285,28 +247,45 @@ export function registerFusionTools(pi: ExtensionAPI, deps: FusionToolDeps): voi
|
|
|
285
247
|
const selected = runtime.reports.get(id);
|
|
286
248
|
if (selected) {
|
|
287
249
|
completion.consume(id);
|
|
288
|
-
return result(reportText(selected), selected, selected.status !== "completed");
|
|
250
|
+
return result(reportText(selected), { ...selected, owner: "fusion" }, selected.status !== "completed");
|
|
289
251
|
}
|
|
290
252
|
if (id !== latest.id) return result(`No sidekick handoff found for ${id}.`, undefined, true);
|
|
291
|
-
if (params.block !== true) return result(`Handoff ${id} is still running.\n${progressText(runtime, id)}`, { progress: runtime.progress(id), id });
|
|
253
|
+
if (params.block !== true) return result(`Handoff ${id} is still running.\n${progressText(runtime, id)}`, { progress: runtime.progress(id), id, owner: "fusion" });
|
|
292
254
|
deps.onAttach?.(ctx);
|
|
255
|
+
runtime.attachUi?.(ctx.ui as never);
|
|
293
256
|
completion.attach(id);
|
|
294
|
-
const timeoutMs = (params.timeout ??
|
|
295
|
-
const waited = await waitForReport(runtime, id, latest.done, signal, ctx, onUpdate
|
|
257
|
+
const timeoutMs = Math.min(600, params.timeout ?? 30) * 1000;
|
|
258
|
+
const waited = await waitForReport(runtime, id, latest.done, signal, ctx, onUpdate, timeoutMs);
|
|
259
|
+
runtime.detachUi?.();
|
|
296
260
|
if (waited.report) {
|
|
297
261
|
completion.consume(id);
|
|
298
262
|
deps.onReport?.(ctx, waited.report);
|
|
299
|
-
return result(reportText(waited.report), waited.report, waited.report.status !== "completed");
|
|
263
|
+
return result(reportText(waited.report), { ...waited.report, owner: "fusion" }, waited.report.status !== "completed");
|
|
300
264
|
}
|
|
301
265
|
completion.detach(id, latest.done);
|
|
266
|
+
trackBackgroundHandoff(id, latest.done);
|
|
302
267
|
deps.onDetach?.(ctx);
|
|
303
268
|
if (waited.error) return result(`Handoff ${id} failed: ${waited.error}`, undefined, true);
|
|
304
269
|
if (waited.aborted) return result(`Handoff ${id} aborted.`, undefined, true);
|
|
305
|
-
if (waited.interrupted) return result(`A user message arrived while the sidekick (agent_id ${id}) was working.\n${waited.interrupted}`, { progress: runtime.progress(id), id });
|
|
306
|
-
return result(`Handoff ${id} is still running.\n${progressText(runtime, id)}`, { progress: runtime.progress(id), id });
|
|
270
|
+
if (waited.interrupted) return result(`A user message arrived while the sidekick (agent_id ${id}) was working.\n${waited.interrupted}`, { progress: runtime.progress(id), id, owner: "fusion" });
|
|
271
|
+
return result(`Handoff ${id} is still running.\n${progressText(runtime, id)}`, { progress: runtime.progress(id), id, owner: "fusion" });
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
let lastCtx: ExtensionContext | undefined;
|
|
275
|
+
registerSubagentReader("fusion", {
|
|
276
|
+
owns: (id, ctx) => reader.owns(id, ctx),
|
|
277
|
+
latest: (ctx) => reader.latest(ctx),
|
|
278
|
+
read: (params, signal, onUpdate, ctx) => {
|
|
279
|
+
lastCtx = ctx;
|
|
280
|
+
return reader.read(params, signal, onUpdate, ctx);
|
|
307
281
|
},
|
|
308
282
|
});
|
|
309
283
|
|
|
284
|
+
function runtime_reports_owns(d: FusionToolDeps, id: string, ctx: ExtensionContext | undefined): boolean {
|
|
285
|
+
const rt = ctx === undefined ? undefined : d.getRuntime(ctx);
|
|
286
|
+
return rt !== undefined && (rt.reports.has(id) || rt.latest()?.id === id);
|
|
287
|
+
}
|
|
288
|
+
|
|
310
289
|
}
|
|
311
290
|
|
|
312
291
|
export { reportText, progressText };
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Markdown, type Component } from "@earendil-works/pi-tui";
|
|
2
2
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import
|
|
3
|
+
import { renderDelegatedStep, type DelegatedStep } from "@pi-unipi/utility/src/render/delegated.js";
|
|
4
|
+
import type { SidekickStep } from "./sidekick-runtime.js";
|
|
4
5
|
|
|
5
6
|
export interface ThemeLike {
|
|
6
7
|
fg: (color: string, text: string) => string;
|
|
7
8
|
bold: (text: string) => string;
|
|
9
|
+
getBgAnsi?: (key: string) => string;
|
|
10
|
+
/** Colour mode when the caller knows it — the delegated panel uses it for
|
|
11
|
+
* its truecolor/256 rail+fill+fg fallbacks. */
|
|
12
|
+
getColorMode?: () => "truecolor" | "256color";
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
export function duration(ms: number): string {
|
|
@@ -15,51 +20,6 @@ export function markdownText(markdown: string): Component {
|
|
|
15
20
|
return new Markdown(markdown, 0, 0, getMarkdownTheme());
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
export function sidekickWorkingHeader(theme: ThemeLike, progress: Pick<HandoffProgress, "toolCalls" | "startedAt">, label = "sidekick"): string {
|
|
19
|
-
return `${theme.fg("accent", theme.bold(`◆ ${label} working`))} ${theme.fg("dim", `· ${String(progress.toolCalls)} tool calls · ${duration(Date.now() - progress.startedAt)}`)}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class RailComponent implements Component {
|
|
23
|
-
constructor(private readonly inner: Component, private readonly rail: string) {}
|
|
24
|
-
|
|
25
|
-
render(width: number): string[] {
|
|
26
|
-
return this.inner.render(Math.max(1, width - 2)).map((line) => `${this.rail} ${line}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
invalidate(): void {
|
|
30
|
-
this.inner.invalidate?.();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
handleInput(data: string): void {
|
|
34
|
-
this.inner.handleInput?.(data);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function frameSidekick(theme: ThemeLike & { bg: (color: string, text: string) => string }, status: "working" | "completed" | "error", content: Component): Component {
|
|
39
|
-
const railColor = status === "working" ? "accent" : status === "completed" ? "success" : "error";
|
|
40
|
-
const rail = theme.fg(railColor, "▍");
|
|
41
|
-
const boxed = new Box(1, 0, (text) => theme.bg("customMessageBg", text));
|
|
42
|
-
boxed.addChild(new RailComponent(content, rail));
|
|
43
|
-
return boxed;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface TranscriptOptions {
|
|
47
|
-
events: readonly SidekickEvent[];
|
|
48
|
-
droppedEvents?: number;
|
|
49
|
-
header: string;
|
|
50
|
-
expanded: boolean;
|
|
51
|
-
isPartial: boolean;
|
|
52
|
-
report?: { text: string };
|
|
53
|
-
renderText: (markdown: string) => Component;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Non-expanded transcripts show this many most recent events. Windowing is the
|
|
58
|
-
* only cap: a completed handoff renders its events, it is never collapsed into
|
|
59
|
-
* a "N steps" stub.
|
|
60
|
-
*/
|
|
61
|
-
export const TRANSCRIPT_WINDOW = 8;
|
|
62
|
-
|
|
63
23
|
function firstLine(value: string): string {
|
|
64
24
|
return value.split("\n", 1)[0] ?? "";
|
|
65
25
|
}
|
|
@@ -80,44 +40,26 @@ export function primaryArg(name: string, args: Record<string, unknown> | undefin
|
|
|
80
40
|
return typeof value === "string" ? truncate(firstLine(value), 100) : "";
|
|
81
41
|
}
|
|
82
42
|
|
|
83
|
-
|
|
84
|
-
const title = `${event.isError ? theme.fg("error", "✗ ") : ""}${theme.fg("toolTitle", theme.bold(event.name))}`;
|
|
85
|
-
const argument = primaryArg(event.name, event.args);
|
|
86
|
-
const lines = [`${title}${argument.length > 0 ? ` ${theme.fg("accent", argument)}` : ""}`];
|
|
87
|
-
if (!event.done) {
|
|
88
|
-
lines[0] += theme.fg("warning", " ⋯ running");
|
|
89
|
-
} else if (event.output.length > 0) {
|
|
90
|
-
const output = event.output.split("\n");
|
|
91
|
-
const visible = expanded ? output.slice(-40) : output.slice(-3);
|
|
92
|
-
lines.push(...visible.map((line) => theme.fg("toolOutput", truncate(line, 160))));
|
|
93
|
-
}
|
|
94
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function renderSidekickTranscript(theme: ThemeLike, opts: TranscriptOptions): Component {
|
|
98
|
-
const box = new Container();
|
|
99
|
-
box.addChild(new Text(opts.header, 0, 0));
|
|
100
|
-
|
|
101
|
-
const dropped = opts.droppedEvents ?? 0;
|
|
102
|
-
const start = opts.expanded ? 0 : Math.max(0, opts.events.length - TRANSCRIPT_WINDOW);
|
|
103
|
-
if (opts.expanded && dropped > 0) {
|
|
104
|
-
box.addChild(new Text(theme.fg("dim", `… ${String(dropped)} earliest steps dropped`), 0, 0));
|
|
105
|
-
} else if (start > 0) {
|
|
106
|
-
box.addChild(new Text(theme.fg("dim", `… ${String(start + dropped)} earlier steps`), 0, 0));
|
|
107
|
-
}
|
|
43
|
+
// ─── sidekick-step entry renderer (Devin-style merged steps) ────────────────
|
|
108
44
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
45
|
+
export interface StepIdentity {
|
|
46
|
+
/** spacingGroup run — unique per handoff so resumed handoffs don't merge. */
|
|
47
|
+
group?: string;
|
|
48
|
+
/** Panel header drawn once on the group's first position (e.g. "Sidekick"). */
|
|
49
|
+
label?: string;
|
|
50
|
+
}
|
|
113
51
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
52
|
+
/**
|
|
53
|
+
* One sidekick step in the lead chat, drawn by the shared delegated-panel
|
|
54
|
+
* helper: the active render style behind the approved cyan `▏` rail on a
|
|
55
|
+
* single dark-cyan fill. Steps carry `spacingGroup` so adjacent steps of the
|
|
56
|
+
* same handoff join into one continuous panel (`├…└` tree, no blank
|
|
57
|
+
* separators); the optional identity comes from the persisted entry (older
|
|
58
|
+
* entries without one fall back to the shared "sidekick" group).
|
|
59
|
+
*/
|
|
60
|
+
export function renderSidekickStep(step: SidekickStep, expanded: boolean, theme: ThemeLike, identity?: StepIdentity): Component {
|
|
61
|
+
return renderDelegatedStep(step as DelegatedStep, expanded, theme as never, {
|
|
62
|
+
group: identity?.group ?? "sidekick",
|
|
63
|
+
label: identity?.label,
|
|
64
|
+
});
|
|
123
65
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/info-screen",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.21",
|
|
4
4
|
"description": "Dashboard and module registry for Unipi — configurable info overlay with tabbed groups",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
35
|
+
"@pi-unipi/core": "3.0.0-alpha.21"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
# @pi-unipi/input-shortcuts
|
|
2
2
|
|
|
3
|
-
Keyboard shortcuts for Pi's input box. Stash and restore text, undo/redo,
|
|
3
|
+
Keyboard shortcuts for Pi's input box. Stash and restore text, undo/redo, copy the last response, push the editor text to the kanboard backlog, and tab insertion — all via a vim-style chord overlay triggered by `ALT+S`.
|
|
4
4
|
|
|
5
|
-
Press `ALT+S`, a small overlay appears with key hints. Press a key, the action runs. The overlay closes automatically after
|
|
5
|
+
Press `ALT+S`, a small overlay appears with key hints. Press a key, the action runs. The overlay closes automatically after selecting an action, on `ESC`, or on an unknown key.
|
|
6
6
|
|
|
7
7
|
## Shortcuts
|
|
8
8
|
|
|
9
9
|
| Chord | Action | Description |
|
|
10
10
|
|-------|--------|-------------|
|
|
11
|
-
| `ALT+S → S` | Stash/Restore | Save input to stash register, or restore it |
|
|
12
|
-
| `ALT+S → U` | Undo |
|
|
13
|
-
| `ALT+S → R` | Redo |
|
|
14
|
-
| `ALT+S →
|
|
15
|
-
| `ALT+S →
|
|
16
|
-
| `ALT+S →
|
|
17
|
-
| `ALT+S → A → [0-9]` | Append Register | Append from numbered register 0-9 |
|
|
18
|
-
| `ALT+S → A → S` | Append Stash | Append from stash register |
|
|
11
|
+
| `ALT+S → S` | Stash/Restore | Save input to the stash register, or restore it |
|
|
12
|
+
| `ALT+S → U` | Undo | Step back through editor checkpoints |
|
|
13
|
+
| `ALT+S → R` | Redo | Step forward again (an edit invalidates the redo) |
|
|
14
|
+
| `ALT+S → A` | Append Stash | Append the stash text to the input (stash kept) |
|
|
15
|
+
| `ALT+S → Y` | Copy Last Response | Copy the last assistant response to the system clipboard |
|
|
16
|
+
| `ALT+S → K` | Add to Kanboard Backlog | Add the editor text to the project's kanboard backlog as a body-only task (attached file paths in the text are uploaded) |
|
|
19
17
|
| `ALT+I` | Tab Insert | Insert literal tab character |
|
|
20
18
|
|
|
21
19
|
## Commands
|
|
@@ -32,36 +30,26 @@ Every action shows a brief success or error message in the status bar via `ctx.u
|
|
|
32
30
|
|
|
33
31
|
## How It Works
|
|
34
32
|
|
|
35
|
-
###
|
|
33
|
+
### Stash
|
|
36
34
|
|
|
37
35
|
- **Stash register**: 1 register for quick save/restore
|
|
38
|
-
- **
|
|
39
|
-
- **Persistence**: Saved to `.unipi/config/input-shortcuts.json` (per-project, atomic writes)
|
|
36
|
+
- **Persistence**: Saved to `.unipi/config/input-shortcuts.json` (per-project, atomic writes). Files written by older versions (with ten numbered registers 0-9) still load; the register keys are ignored and dropped on the next save.
|
|
40
37
|
|
|
41
38
|
### Undo/Redo
|
|
42
39
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
40
|
+
- Linear checkpoint history, O(1) amortized per record
|
|
41
|
+
- A typing burst records the editor text exactly twice — before the first keystroke and after the last (burst closes after 600ms of quiet, and long bursts split every ~2s) — so undo works from the very first keystroke without reading the editor on every keypress
|
|
42
|
+
- At most 100 checkpoints / 2M characters in memory (oldest evicted first)
|
|
43
|
+
- An edit after an undo invalidates the redo; undoing captures un-flushed typing first
|
|
47
44
|
- Not persisted across sessions
|
|
48
45
|
|
|
49
|
-
###
|
|
46
|
+
### Copy Last Response
|
|
50
47
|
|
|
51
|
-
|
|
48
|
+
Uses pi's own cross-platform `copyToClipboard` (wl-copy / xclip / pbcopy / Windows / WSL / OSC 52 fallbacks) on the text of the last assistant response — the same text pi's `/copy` copies.
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|----------|------|-------|
|
|
55
|
-
| Linux (X11) | `xclip -selection clipboard -o` | `xclip -selection clipboard` |
|
|
56
|
-
| Linux (alt) | `xsel --clipboard --output` | `xsel --clipboard --input` |
|
|
57
|
-
| macOS | `pbpaste` | `pbcopy` |
|
|
58
|
-
| Windows | `powershell Get-Clipboard` | `clip` / `powershell Set-Clipboard` |
|
|
50
|
+
### Kanboard Backlog
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### Thinking Toggle
|
|
63
|
-
|
|
64
|
-
Cycles through Pi's thinking levels: off → low → medium → high → xhigh → off.
|
|
52
|
+
The K chord hands the editor text to `globalThis.__unipi_kanboard_api.captureToBacklog()` (registered by `@pi-unipi/kanboard`): the text becomes a body-only Backlog task, and file paths in the text (pasted/dragged files and pasted images, which pi inserts as paths) are attached. Refuses with a status message when kanboard is not loaded or the folder is not onboarded.
|
|
65
53
|
|
|
66
54
|
## Configurables
|
|
67
55
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/input-shortcuts",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.21",
|
|
4
4
|
"description": "Keyboard shortcuts for stash/restore, undo/redo, clipboard, and thinking toggle — chord-based overlay system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
36
|
+
"@pi-unipi/core": "3.0.0-alpha.21"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|