@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
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* Long-horizon commands — the explicit mode triggers.
|
|
3
3
|
*
|
|
4
4
|
* /unipi:goal|ralph|swarm|graph <prompt> turn override + prompt
|
|
5
|
+
* /unipi:goal|swarm|graph stop END the active owner (terminal)
|
|
6
|
+
* /unipi:ralph stop park the active loop
|
|
5
7
|
* /unipi:goal|ralph|swarm|graph status owner + mode snapshot
|
|
6
8
|
* /unipi:goal|ralph|swarm|graph resume reactivate the parked owner
|
|
7
9
|
* /unipi:goal|ralph|swarm|graph clear drop the parked owner
|
|
10
|
+
* /unipi:regular stop any owner, pin regular mode
|
|
8
11
|
* /unipi:continue resume the parked owner
|
|
9
12
|
*
|
|
10
13
|
* Switching while an owner is active suspends it (max-1 park slot; the
|
|
@@ -13,14 +16,23 @@
|
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
16
|
-
import { emitEvent, UNIPI_EVENTS } from "@pi-unipi/core";
|
|
17
|
-
import type { LhMode } from "./modes.js";
|
|
19
|
+
import { appendProgress, emitEvent, sendHarnessUserMessage, UNIPI_EVENTS, type ProgressData } from "@pi-unipi/core";
|
|
20
|
+
import type { LhMode, OwnerKind } from "./modes.js";
|
|
18
21
|
import { MODE_REGISTRY } from "./modes.js";
|
|
19
22
|
import type { Gate } from "./gate.js";
|
|
20
23
|
import type { OwnerCoordinator } from "./owner.js";
|
|
21
24
|
import type { RalphLoop } from "./engine/ralph.js";
|
|
25
|
+
import type { GoalMachine } from "./engine/goal-state.js";
|
|
26
|
+
import type { GoalToolset } from "./tools/goal.js";
|
|
22
27
|
import { loadSettings } from "./settings.js";
|
|
23
28
|
|
|
29
|
+
/** Progress-bar hooks (all output is user-only; see src/progress.ts). */
|
|
30
|
+
export interface ProgressHooks {
|
|
31
|
+
ralphBar(): ProgressData | undefined;
|
|
32
|
+
estimateGoal(ctx: ExtensionCommandContext): Promise<"ok" | "none" | "failed">;
|
|
33
|
+
goalEstimateOn(): boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export interface LongHorizonCommandDeps {
|
|
25
37
|
readonly gate: Gate;
|
|
26
38
|
readonly owner: OwnerCoordinator;
|
|
@@ -32,6 +44,37 @@ function notify(ctx: ExtensionCommandContext, text: string): void {
|
|
|
32
44
|
if (ctx.hasUI) ctx.ui.notify(text, "info");
|
|
33
45
|
}
|
|
34
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Terminally end the active owner (optionally only one kind): goal marked
|
|
49
|
+
* complete(user_requested), pending proposal dropped, owner → history, session
|
|
50
|
+
* pinned to regular mode. Returns the stopped owner id, undefined when nothing
|
|
51
|
+
* (of that kind) is active. Shared by /unipi:<mode> stop, /unipi:regular, and
|
|
52
|
+
* the unipi:goal-stop runner.
|
|
53
|
+
*/
|
|
54
|
+
export function stopActiveOwner(
|
|
55
|
+
pi: ExtensionAPI,
|
|
56
|
+
gate: Gate,
|
|
57
|
+
owner: OwnerCoordinator,
|
|
58
|
+
machine?: GoalMachine,
|
|
59
|
+
toolset?: GoalToolset,
|
|
60
|
+
kind?: OwnerKind,
|
|
61
|
+
): string | undefined {
|
|
62
|
+
const active = owner.getActive();
|
|
63
|
+
if (!active || (kind !== undefined && active.kind !== kind)) return undefined;
|
|
64
|
+
if (active.kind === "goal") {
|
|
65
|
+
machine?.clear();
|
|
66
|
+
toolset?.discardProposal();
|
|
67
|
+
}
|
|
68
|
+
const finished = owner.finish("stopped(user_requested)");
|
|
69
|
+
emitEvent(pi, UNIPI_EVENTS.LONG_HORIZON_OWNER_CHANGED, {
|
|
70
|
+
event: "stopped",
|
|
71
|
+
kind: active.kind,
|
|
72
|
+
...(finished ? { ownerId: finished.ownerId } : {}),
|
|
73
|
+
});
|
|
74
|
+
gate.setSessionMode("none");
|
|
75
|
+
return finished?.ownerId ?? active.ownerId;
|
|
76
|
+
}
|
|
77
|
+
|
|
35
78
|
function ownerSnapshotText(owner: OwnerCoordinator): string {
|
|
36
79
|
const active = owner.getActive();
|
|
37
80
|
const parked = owner.getParked();
|
|
@@ -55,10 +98,10 @@ function ownerSnapshotText(owner: OwnerCoordinator): string {
|
|
|
55
98
|
|
|
56
99
|
/** Short user-facing mode descriptions: use case + cost/success (design §2 rubric). */
|
|
57
100
|
const MODE_DESCRIPTIONS = {
|
|
58
|
-
goal: "One objective until verifiably true. Use: medium-complex single deliverables. Cost/success: pareto per success. (<prompt> | status | resume | clear)",
|
|
101
|
+
goal: "One objective until verifiably true. Use: medium-complex single deliverables. Cost/success: pareto per success. (<prompt> | status | stop | resume | clear)",
|
|
59
102
|
ralph: "Checklist grind over iterations. Use: enumerable chores, repo-scale plans. Cost/success: low cost, solid success. (start <name> | stop | status | resume | clear | <prompt>)",
|
|
60
|
-
swarm: "Parallel fan-out + one synthesis. Use: complex decomposable work. Cost/success: higher cost, high coverage. (<prompt> | status | resume | clear)",
|
|
61
|
-
graph: "Dependent multi-step work. Use: later steps need earlier results. Cost/success: highest; run when the shape demands it. (<prompt> | status | resume | clear)",
|
|
103
|
+
swarm: "Parallel fan-out + one synthesis. Use: complex decomposable work. Cost/success: higher cost, high coverage. (<prompt> | status | stop | resume | clear)",
|
|
104
|
+
graph: "Dependent multi-step work. Use: later steps need earlier results. Cost/success: highest; run when the shape demands it. (<prompt> | status | stop | resume | clear)",
|
|
62
105
|
} as const;
|
|
63
106
|
|
|
64
107
|
interface CompletionItem {
|
|
@@ -71,6 +114,7 @@ interface CompletionItem {
|
|
|
71
114
|
const goalCompletions = (prefix: string): CompletionItem[] => {
|
|
72
115
|
const subs: CompletionItem[] = [
|
|
73
116
|
{ value: "status", label: "status", description: "show owner + mode snapshot" },
|
|
117
|
+
{ value: "stop", label: "stop", description: "end the active owner (terminal)" },
|
|
74
118
|
{ value: "resume", label: "resume", description: "reactivate the parked owner" },
|
|
75
119
|
{ value: "clear", label: "clear", description: "drop the parked owner" },
|
|
76
120
|
];
|
|
@@ -98,7 +142,13 @@ export function registerLongHorizonCommands(
|
|
|
98
142
|
gate: Gate,
|
|
99
143
|
owner: OwnerCoordinator,
|
|
100
144
|
ralph?: RalphLoop,
|
|
145
|
+
progressHooks?: ProgressHooks,
|
|
146
|
+
machine?: GoalMachine,
|
|
147
|
+
toolset?: GoalToolset,
|
|
101
148
|
): void {
|
|
149
|
+
/** Kept for the modeHandler closure — see exported stopActiveOwner. */
|
|
150
|
+
const stopActiveOwnerFor = (kind?: OwnerKind): string | undefined =>
|
|
151
|
+
stopActiveOwner(pi, gate, owner, machine, toolset, kind);
|
|
102
152
|
const modeHandler = (mode: LhMode) => async (args: string, ctx: ExtensionCommandContext): Promise<void> => {
|
|
103
153
|
const definition = MODE_REGISTRY[mode];
|
|
104
154
|
const parts = args.trim().split(/\s+/);
|
|
@@ -146,6 +196,22 @@ export function registerLongHorizonCommands(
|
|
|
146
196
|
? `Loop "${loopState.name}": iteration ${loopState.iteration}, ${progress.checked}/${progress.total} items checked.\nNext: ${progress.next.slice(0, 3).join(" · ") || "(all checked)"}\n${ownerSnapshotText(owner)}`
|
|
147
197
|
: ownerSnapshotText(owner),
|
|
148
198
|
);
|
|
199
|
+
const bar = progress.total > 0 ? progressHooks?.ralphBar() : undefined;
|
|
200
|
+
if (bar) appendProgress(pi, bar);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// stop — terminally END the active owner (goal/swarm/graph; ralph's stop
|
|
205
|
+
// stays park). A subcommand, never a prompt: it must not fall through to
|
|
206
|
+
// the <prompt> branch below.
|
|
207
|
+
if (mode !== "ralph" && sub === "stop") {
|
|
208
|
+
const stopped = stopActiveOwnerFor(mode as OwnerKind);
|
|
209
|
+
notify(
|
|
210
|
+
ctx,
|
|
211
|
+
stopped !== undefined
|
|
212
|
+
? `${definition.label} stopped.`
|
|
213
|
+
: `No active ${definition.label.toLowerCase()}.`,
|
|
214
|
+
);
|
|
149
215
|
return;
|
|
150
216
|
}
|
|
151
217
|
|
|
@@ -158,6 +224,13 @@ export function registerLongHorizonCommands(
|
|
|
158
224
|
`\nJudge: ${settings.judge.enabled ? `${settings.judge.provider}/${settings.judge.model} (threshold ${settings.judge.threshold})` : "off"} — default mode: ${settings.defaultMode}` +
|
|
159
225
|
`\nSettings: ~/.pi/agent/settings.json → unipi.longHorizon`,
|
|
160
226
|
);
|
|
227
|
+
// /goal status: estimate progress + summary in a side call (user-only).
|
|
228
|
+
if (mode === "goal" && progressHooks?.goalEstimateOn()) {
|
|
229
|
+
ctx.ui.setStatus?.("lh-progress", "◎ estimating goal progress…");
|
|
230
|
+
const outcome = await progressHooks.estimateGoal(ctx);
|
|
231
|
+
ctx.ui.setStatus?.("lh-progress", undefined);
|
|
232
|
+
if (outcome === "failed") notify(ctx, "Goal progress estimate failed (model unavailable or unparseable reply).");
|
|
233
|
+
}
|
|
161
234
|
return;
|
|
162
235
|
}
|
|
163
236
|
|
|
@@ -174,17 +247,32 @@ export function registerLongHorizonCommands(
|
|
|
174
247
|
emitEvent(pi, UNIPI_EVENTS.LONG_HORIZON_OWNER_CHANGED, { event: "resumed", ownerId: resumed.ownerId });
|
|
175
248
|
const resumeMode = resumed.kind === "ralph-loop" ? "ralph" : resumed.kind;
|
|
176
249
|
gate.setExplicit(resumeMode as LhMode);
|
|
177
|
-
await
|
|
250
|
+
await sendHarnessUserMessage(
|
|
251
|
+
pi,
|
|
252
|
+
"Continue the resumed owner from its own durable state; re-read its status before acting.",
|
|
253
|
+
{ source: "Long-horizon", title: "Resume", synopsis: "Owner resumed from durable state" },
|
|
254
|
+
);
|
|
178
255
|
return;
|
|
179
256
|
}
|
|
180
257
|
|
|
181
258
|
if (sub === "clear") {
|
|
259
|
+
const active = owner.getActive();
|
|
260
|
+
if (active) {
|
|
261
|
+
const noun = active.kind === "ralph-loop" ? "ralph loop" : active.kind;
|
|
262
|
+
notify(
|
|
263
|
+
ctx,
|
|
264
|
+
active.kind === "ralph-loop"
|
|
265
|
+
? "A ralph loop is running — /unipi:ralph stop parks it, or /unipi:regular ends it."
|
|
266
|
+
: `A ${noun} is running — use /unipi:${noun} stop to end it.`,
|
|
267
|
+
);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
182
270
|
const cleared = owner.clearParked();
|
|
183
271
|
notify(
|
|
184
272
|
ctx,
|
|
185
273
|
cleared
|
|
186
274
|
? `Cleared parked ${cleared.kind} "${cleared.label}".`
|
|
187
|
-
: "Nothing
|
|
275
|
+
: "Nothing to clear.",
|
|
188
276
|
);
|
|
189
277
|
if (cleared) {
|
|
190
278
|
emitEvent(pi, UNIPI_EVENTS.LONG_HORIZON_OWNER_CHANGED, { event: "cleared", ownerId: cleared.ownerId });
|
|
@@ -221,5 +309,13 @@ export function registerLongHorizonCommands(
|
|
|
221
309
|
pi.registerCommand("unipi:ralph", { description: MODE_DESCRIPTIONS.ralph, getArgumentCompletions: ralphCompletions, handler: modeHandler("ralph") });
|
|
222
310
|
pi.registerCommand("unipi:swarm", { description: MODE_DESCRIPTIONS.swarm, getArgumentCompletions: goalCompletions, handler: modeHandler("swarm") });
|
|
223
311
|
pi.registerCommand("unipi:graph", { description: MODE_DESCRIPTIONS.graph, getArgumentCompletions: goalCompletions, handler: modeHandler("graph") });
|
|
312
|
+
pi.registerCommand("unipi:regular", {
|
|
313
|
+
description: "Regular mode — stop any active owner and run prompts without long-horizon routing",
|
|
314
|
+
handler: async (_args: string, ctx: ExtensionCommandContext): Promise<void> => {
|
|
315
|
+
stopActiveOwnerFor();
|
|
316
|
+
gate.setSessionMode("none");
|
|
317
|
+
notify(ctx, "Regular mode.");
|
|
318
|
+
},
|
|
319
|
+
});
|
|
224
320
|
|
|
225
321
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active-work brief for compaction summaries: what the goal / ralph loop is
|
|
3
|
+
* doing right now, from durable state rather than transcript guesses.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { GoalState } from "./engine/goal-state.js";
|
|
7
|
+
import type { LoopFileState } from "./engine/ralph.js";
|
|
8
|
+
|
|
9
|
+
const clip = (text: string, max: number): string => {
|
|
10
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
11
|
+
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function longHorizonCompactionBrief(
|
|
15
|
+
goal: GoalState | null,
|
|
16
|
+
loop: LoopFileState | null,
|
|
17
|
+
ralphDir: string,
|
|
18
|
+
): string | null {
|
|
19
|
+
const lines: string[] = [];
|
|
20
|
+
if (loop && loop.status === "active") {
|
|
21
|
+
const max = loop.maxIterations > 0 ? `/${loop.maxIterations}` : "";
|
|
22
|
+
lines.push(
|
|
23
|
+
`Ralph loop "${loop.name}" is running — iteration ${loop.iteration}${max}.`,
|
|
24
|
+
`Task file (the checklist is the source of truth, re-read it): ${ralphDir}/${loop.taskFile}`,
|
|
25
|
+
"Work the next unchecked items, update the task file, then call ralph_done.",
|
|
26
|
+
);
|
|
27
|
+
} else if (goal && goal.status === "active") {
|
|
28
|
+
const stall = goal.noProgressStreak > 0 ? `, ${goal.noProgressStreak}/${goal.stallCap} no-progress` : "";
|
|
29
|
+
lines.push(
|
|
30
|
+
`Goal (active, turn ${goal.turn}/${goal.maxTurns}${stall}): "${clip(goal.objective, 700)}"`,
|
|
31
|
+
"Keep working toward it; call get_goal for the durable state. Completion is judged by an independent verifier.",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return lines.length > 0 ? lines.join("\n") : null;
|
|
35
|
+
}
|
|
@@ -55,8 +55,20 @@ export interface ContinuationDeps {
|
|
|
55
55
|
readonly owner: OwnerCoordinator;
|
|
56
56
|
/** Verifier wiring; defaults to a VerifierDeps the caller provides. */
|
|
57
57
|
readonly verifier: VerifierDeps;
|
|
58
|
-
/**
|
|
59
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Deliver a continuation/kickoff/wrap-up message. Messages go to the
|
|
60
|
+
* arbiter's nudge stash (long-horizon/index.ts), NOT the message queue;
|
|
61
|
+
* `kind: "kickoff"` marks the kickoff contract, which is never overwritten
|
|
62
|
+
* while undelivered.
|
|
63
|
+
*/
|
|
64
|
+
send(message: string, kind?: "kickoff"): void;
|
|
65
|
+
/**
|
|
66
|
+
* Direct delivery for TIMER wakes while idle (waiting-backoff): an event,
|
|
67
|
+
* not a nudge — bypasses the stash and prompts immediately.
|
|
68
|
+
*/
|
|
69
|
+
sendNow?(message: string): void;
|
|
70
|
+
/** User-only notification (wired to ctx.ui.notify at agent_end; optional in tests). */
|
|
71
|
+
notify?(text: string, level?: "info" | "warning" | "error"): void;
|
|
60
72
|
getTokenCount?(): number | undefined;
|
|
61
73
|
now?(): number;
|
|
62
74
|
schedule?(callback: () => void, delayMs: number): void;
|
|
@@ -108,6 +120,23 @@ export class GoalContinuation {
|
|
|
108
120
|
this.evaluateOverride = evaluate;
|
|
109
121
|
}
|
|
110
122
|
|
|
123
|
+
private tokensEstimated = false;
|
|
124
|
+
private notifyOverride?: NonNullable<ContinuationDeps["notify"]>;
|
|
125
|
+
|
|
126
|
+
/** Late-bound: the last turn's tokens were estimated, not read from usage. */
|
|
127
|
+
setTokensEstimated(estimated: boolean): void {
|
|
128
|
+
this.tokensEstimated = estimated;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Late-bound user notification (ctx.ui.notify at agent_end). */
|
|
132
|
+
setNotify(notify: NonNullable<ContinuationDeps["notify"]>): void {
|
|
133
|
+
this.notifyOverride = notify;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private inform(text: string, level: "info" | "warning" | "error" = "warning"): void {
|
|
137
|
+
(this.notifyOverride ?? this.deps.notify)?.(text, level);
|
|
138
|
+
}
|
|
139
|
+
|
|
111
140
|
/** Crash/interrupt marker: the next continuation carries the recovery fragment. */
|
|
112
141
|
armRecovery(): void {
|
|
113
142
|
this.recoveryArmed = true;
|
|
@@ -115,7 +144,7 @@ export class GoalContinuation {
|
|
|
115
144
|
|
|
116
145
|
/** Deliver the kickoff contract once for a fresh goal (cache-stable). */
|
|
117
146
|
deliverKickoff(goal: GoalState): void {
|
|
118
|
-
this.deps.send(renderKickoff(goal.objective));
|
|
147
|
+
this.deps.send(renderKickoff(goal.objective), "kickoff");
|
|
119
148
|
this.deps.machine.markKickoffDelivered(goal.goalId);
|
|
120
149
|
this.consecutiveWaits = 0;
|
|
121
150
|
this.lastVerifierReason = undefined;
|
|
@@ -128,11 +157,19 @@ export class GoalContinuation {
|
|
|
128
157
|
async onTurnEnd(activity: TurnActivity): Promise<ContinuationDecision> {
|
|
129
158
|
const machine = this.deps.machine;
|
|
130
159
|
const goal = machine.getActive();
|
|
131
|
-
if (!goal)
|
|
160
|
+
if (!goal) {
|
|
161
|
+
// No drivable goal: a pending proposal could never be settled — drop it
|
|
162
|
+
// so get_goal stops advertising "verification pending" forever.
|
|
163
|
+
this.deps.toolset.discardProposal();
|
|
164
|
+
return { action: "none", reason: "no-active-goal" };
|
|
165
|
+
}
|
|
132
166
|
|
|
133
167
|
// Kickoff not yet delivered (e.g., goal created mid-turn): contract first.
|
|
168
|
+
// The kickoff turn never settles — a proposal made beside creation is
|
|
169
|
+
// stale by definition; discard it instead of letting it outlive the turn.
|
|
134
170
|
if (!goal.kickoffDelivered) {
|
|
135
171
|
this.deliverKickoff(goal);
|
|
172
|
+
this.deps.toolset.discardProposal();
|
|
136
173
|
return { action: "none", reason: "kickoff-delivered" };
|
|
137
174
|
}
|
|
138
175
|
|
|
@@ -149,11 +186,16 @@ export class GoalContinuation {
|
|
|
149
186
|
commands: activity.commands,
|
|
150
187
|
recentTail: activity.recentTail,
|
|
151
188
|
});
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
189
|
+
const verify = () =>
|
|
190
|
+
verifyCompletion(
|
|
191
|
+
{ ...this.deps.verifier, ...(this.evaluateOverride ? { evaluate: this.evaluateOverride } : {}) },
|
|
192
|
+
goal.objective,
|
|
193
|
+
brief,
|
|
194
|
+
);
|
|
195
|
+
// One transient evaluator failure gets a retry inside the same
|
|
196
|
+
// settlement; a second failure is `verifierUnavailable`, never a verdict.
|
|
197
|
+
let verdict = await verify();
|
|
198
|
+
if (verdict.evaluatorFailed) verdict = await verify();
|
|
157
199
|
this.lastVerifierReason = verdict.evaluatorFailed
|
|
158
200
|
? undefined
|
|
159
201
|
: `${verdict.reason}${verdict.missing.length > 0 ? ` (missing: ${verdict.missing.join("; ")})` : ""}`;
|
|
@@ -162,11 +204,27 @@ export class GoalContinuation {
|
|
|
162
204
|
revision: goal.revision,
|
|
163
205
|
completionClaim: { ...(proposal.summary !== undefined ? { summary: proposal.summary } : {}) },
|
|
164
206
|
verifier: { verdict: verdict.verdict, ...(verdict.missing.length > 0 ? { missing: verdict.missing } : {}) },
|
|
165
|
-
|
|
166
|
-
//
|
|
167
|
-
|
|
207
|
+
...(verdict.evaluatorFailed ? { verifierUnavailable: true } : {}),
|
|
208
|
+
// A rejected claim is no progress unless the turn also changed files
|
|
209
|
+
// (work done, claimed early — common in batched goals); a met claim
|
|
210
|
+
// completed above; an inconclusive/failed verifier is neutral.
|
|
211
|
+
madeProgress: verdict.verdict === "not_met" ? activity.changedFiles.length > 0 : undefined,
|
|
168
212
|
...(tokensNow !== undefined ? { tokensNow } : {}),
|
|
213
|
+
...(tokensNow !== undefined ? { tokensEstimated: this.tokensEstimated } : {}),
|
|
169
214
|
});
|
|
215
|
+
this.tokensEstimated = false;
|
|
216
|
+
if (settled?.status === "paused") {
|
|
217
|
+
if (settled.reason === "paused(verifier_unavailable)") {
|
|
218
|
+
this.inform(`Goal verifier unavailable twice (${verdict.reason}) — goal paused. /unipi:goal resume to retry.`);
|
|
219
|
+
} else if (settled.reason === "paused(verifier_inconclusive)") {
|
|
220
|
+
this.inform(
|
|
221
|
+
`Goal verifier returned inconclusive twice — goal paused. Last evaluation: ${verdict.reason}. /unipi:goal resume.`,
|
|
222
|
+
);
|
|
223
|
+
} else if (settled.reason === "paused(no_progress_after_completion_claim)") {
|
|
224
|
+
const missing = verdict.missing.length > 0 ? ` Missing: ${verdict.missing.join("; ")}.` : "";
|
|
225
|
+
this.inform(`Completion claim rejected ${settled.notMetStreak} times — goal paused.${missing} /unipi:goal resume.`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
170
228
|
} else if (proposal?.kind === "blocked") {
|
|
171
229
|
settled = machine.settleTurn({
|
|
172
230
|
goalId: goal.goalId,
|
|
@@ -184,7 +242,9 @@ export class GoalContinuation {
|
|
|
184
242
|
madeProgress: activity.toolCalls > 0 ? true : undefined,
|
|
185
243
|
...(activity.waiting ? { waiting: true } : {}),
|
|
186
244
|
...(tokensNow !== undefined ? { tokensNow } : {}),
|
|
245
|
+
...(tokensNow !== undefined ? { tokensEstimated: this.tokensEstimated } : {}),
|
|
187
246
|
});
|
|
247
|
+
this.tokensEstimated = false;
|
|
188
248
|
}
|
|
189
249
|
|
|
190
250
|
if (!settled) {
|
|
@@ -192,12 +252,19 @@ export class GoalContinuation {
|
|
|
192
252
|
return { action: "none", reason: "no-active-goal" };
|
|
193
253
|
}
|
|
194
254
|
|
|
195
|
-
// Terminal
|
|
255
|
+
// Terminal → stop the owner; PAUSED → park it (resumable via
|
|
256
|
+
// /unipi:goal resume); budget_limited adds a wrap-up turn.
|
|
196
257
|
if (settled.status !== "active" && settled.status !== "waiting") {
|
|
197
258
|
const terminal = settled.reason ?? settled.status;
|
|
198
259
|
const wrapUp = settled.status === "budget_limited" && !machine.isWrapUpDelivered(settled);
|
|
199
260
|
if (wrapUp) machine.markWrapUpDelivered(settled.goalId, settled.revision);
|
|
200
|
-
|
|
261
|
+
if (settled.status === "paused") {
|
|
262
|
+
// Park (resume keeps the same goal); fall back to finish if the
|
|
263
|
+
// single park slot is held — the loop must still stop.
|
|
264
|
+
if (!this.deps.owner.suspend(terminal)) this.deps.owner.finish(terminal);
|
|
265
|
+
} else {
|
|
266
|
+
this.deps.owner.finish(terminal);
|
|
267
|
+
}
|
|
201
268
|
if (wrapUp) this.deps.send(WRAP_UP_PROMPT);
|
|
202
269
|
return { action: "stopped", terminal, wrapUp };
|
|
203
270
|
}
|
|
@@ -210,7 +277,9 @@ export class GoalContinuation {
|
|
|
210
277
|
const woken = this.deps.machine.wakeWaiting();
|
|
211
278
|
if (woken) {
|
|
212
279
|
this.consecutiveWaits = 0;
|
|
213
|
-
|
|
280
|
+
// Timer wake while idle: an event, delivered directly — never
|
|
281
|
+
// through the stash (nothing will settle to drain it).
|
|
282
|
+
(this.deps.sendNow ?? this.deps.send)(renderContinuationHint(woken));
|
|
214
283
|
}
|
|
215
284
|
}, delayMs);
|
|
216
285
|
return { action: "wait", delayMs };
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* can neither fake progress nor trip the stall detector)
|
|
17
17
|
* - worker completion proposals never complete directly: only verifier met
|
|
18
18
|
* or user request does (propose + verify). A rejected claim counts as
|
|
19
|
-
* no-progress for that turn and feeds
|
|
19
|
+
* no-progress for that turn (unless the turn changed files) and feeds
|
|
20
|
+
* notMetStreak.
|
|
20
21
|
* - blocked requires 3 consecutive blocked proposals (mcode threshold);
|
|
21
22
|
* safety/policy refusals are immediate
|
|
22
23
|
* - every settlement carries {goalId, revision} — CAS; stale settlements
|
|
@@ -55,6 +56,7 @@ export const GOAL_STATUS_REASONS = [
|
|
|
55
56
|
"paused(user_requested)",
|
|
56
57
|
"paused(superseded)",
|
|
57
58
|
"paused(verifier_unavailable)",
|
|
59
|
+
"paused(verifier_inconclusive)",
|
|
58
60
|
"paused(no_progress)",
|
|
59
61
|
"paused(no_progress_after_completion_claim)",
|
|
60
62
|
"blocked(threshold_3turns)",
|
|
@@ -76,7 +78,9 @@ export const STALL_CAP_CEILING = 50;
|
|
|
76
78
|
export const TOKEN_BUDGET_MINIMUM = 1_000;
|
|
77
79
|
export const BLOCKED_PROPOSAL_THRESHOLD = 3;
|
|
78
80
|
/** mcode: a verifier that keeps saying not_met this many times after claims parks the goal. */
|
|
79
|
-
export const NOT_MET_STREAK_CAP =
|
|
81
|
+
export const NOT_MET_STREAK_CAP = 5;
|
|
82
|
+
/** Consecutive inconclusive verdicts before the goal pauses for a human call. */
|
|
83
|
+
export const INCONCLUSIVE_STREAK_CAP = 2;
|
|
80
84
|
|
|
81
85
|
export const GOAL_CONDITION_LIMIT = { codeUnits: 500, utf8Bytes: 1_500 } as const;
|
|
82
86
|
|
|
@@ -96,6 +100,8 @@ export interface GoalState {
|
|
|
96
100
|
readonly tokenBudget: number | null;
|
|
97
101
|
/** Written at the first settlement that observes tokensNow. */
|
|
98
102
|
readonly tokensBaselinePending: boolean;
|
|
103
|
+
/** True when the current tokens figure was estimated (no provider usage). */
|
|
104
|
+
readonly tokensEstimated?: boolean;
|
|
99
105
|
readonly tokensAtStart: number;
|
|
100
106
|
readonly tokensNow: number;
|
|
101
107
|
readonly turn: number;
|
|
@@ -104,6 +110,8 @@ export interface GoalState {
|
|
|
104
110
|
readonly stallCap: number;
|
|
105
111
|
/** Consecutive verifier not_met verdicts on completion claims. */
|
|
106
112
|
readonly notMetStreak: number;
|
|
113
|
+
/** Consecutive inconclusive verdicts (weak evidence — neither met nor not_met). */
|
|
114
|
+
readonly inconclusiveStreak: number;
|
|
107
115
|
/** Consecutive blocked proposals (blocked needs 3 in a row). */
|
|
108
116
|
readonly blockedProposalStreak: number;
|
|
109
117
|
readonly lease: GoalLease;
|
|
@@ -133,6 +141,10 @@ export interface GoalSettlement {
|
|
|
133
141
|
readonly blockedProposal?: boolean;
|
|
134
142
|
/** Safety/policy refusal — immediately terminal. */
|
|
135
143
|
readonly safetyRefusal?: boolean;
|
|
144
|
+
/** The evaluator itself failed twice (error/timeout/unparseable) — pause, don't guess. */
|
|
145
|
+
readonly verifierUnavailable?: boolean;
|
|
146
|
+
/** tokensNow was estimated from message sizes, not read from provider usage. */
|
|
147
|
+
readonly tokensEstimated?: boolean;
|
|
136
148
|
}
|
|
137
149
|
|
|
138
150
|
export interface GoalCreateOptions {
|
|
@@ -233,11 +245,13 @@ export class GoalMachine {
|
|
|
233
245
|
tokensBaselinePending: true,
|
|
234
246
|
tokensAtStart: 0,
|
|
235
247
|
tokensNow: 0,
|
|
248
|
+
tokensEstimated: false,
|
|
236
249
|
turn: 0,
|
|
237
250
|
maxTurns: Math.min(options.maxTurns ?? DEFAULT_MAX_TURNS, MAX_TURNS_CEILING),
|
|
238
251
|
noProgressStreak: 0,
|
|
239
252
|
stallCap: Math.min(options.stallCap ?? DEFAULT_STALL_CAP, STALL_CAP_CEILING),
|
|
240
253
|
notMetStreak: 0,
|
|
254
|
+
inconclusiveStreak: 0,
|
|
241
255
|
blockedProposalStreak: 0,
|
|
242
256
|
lease: Object.freeze({ goalId, generation: 0 }),
|
|
243
257
|
revision: 0,
|
|
@@ -338,33 +352,68 @@ export class GoalMachine {
|
|
|
338
352
|
let turn = goal.turn;
|
|
339
353
|
let noProgressStreak = goal.noProgressStreak;
|
|
340
354
|
let notMetStreak = goal.notMetStreak;
|
|
355
|
+
let inconclusiveStreak = goal.inconclusiveStreak ?? 0;
|
|
341
356
|
let blockedProposalStreak = goal.blockedProposalStreak;
|
|
342
357
|
|
|
343
358
|
// 1. Verification of a completion claim decides completion.
|
|
344
359
|
if (input.completionClaim && input.verifier) {
|
|
345
|
-
|
|
346
|
-
|
|
360
|
+
// 1a. The evaluator itself failed twice in this settlement: pause — an
|
|
361
|
+
// unproven verdict must never advance (or stall) the goal.
|
|
362
|
+
if (input.verifierUnavailable) {
|
|
363
|
+
const next = this.withGoal(goal, {
|
|
364
|
+
status: "paused",
|
|
365
|
+
reason: "paused(verifier_unavailable)",
|
|
366
|
+
inconclusiveStreak: 0,
|
|
367
|
+
...(input.tokensEstimated !== undefined ? { tokensEstimated: input.tokensEstimated } : {}),
|
|
368
|
+
});
|
|
347
369
|
this.goal = next;
|
|
348
370
|
this.commit(next, goal.status);
|
|
349
371
|
return next;
|
|
350
372
|
}
|
|
351
|
-
if (input.verifier.verdict === "
|
|
352
|
-
const next = this.withGoal(goal, { status: "
|
|
373
|
+
if (input.verifier.verdict === "met") {
|
|
374
|
+
const next = this.withGoal(goal, { status: "complete", reason: "complete(verifier_met)", inconclusiveStreak: 0 });
|
|
353
375
|
this.goal = next;
|
|
354
376
|
this.commit(next, goal.status);
|
|
355
377
|
return next;
|
|
356
378
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (notMetStreak >= NOT_MET_STREAK_CAP && noProgressStreak + 1 >= goal.stallCap) {
|
|
360
|
-
const next = this.withGoal(goal, {
|
|
361
|
-
status: "paused",
|
|
362
|
-
reason: "paused(no_progress_after_completion_claim)",
|
|
363
|
-
});
|
|
379
|
+
if (input.verifier.verdict === "impossible") {
|
|
380
|
+
const next = this.withGoal(goal, { status: "blocked", reason: "blocked(verifier_impossible)", inconclusiveStreak: 0 });
|
|
364
381
|
this.goal = next;
|
|
365
382
|
this.commit(next, goal.status);
|
|
366
383
|
return next;
|
|
367
384
|
}
|
|
385
|
+
// not_met: the claim failed. Count it; the cap parks unconditionally.
|
|
386
|
+
if (input.verifier.verdict === "not_met") {
|
|
387
|
+
notMetStreak = notMetStreak + 1;
|
|
388
|
+
inconclusiveStreak = 0;
|
|
389
|
+
if (notMetStreak >= NOT_MET_STREAK_CAP) {
|
|
390
|
+
const next = this.withGoal(goal, {
|
|
391
|
+
status: "paused",
|
|
392
|
+
reason: "paused(no_progress_after_completion_claim)",
|
|
393
|
+
notMetStreak,
|
|
394
|
+
inconclusiveStreak,
|
|
395
|
+
});
|
|
396
|
+
this.goal = next;
|
|
397
|
+
this.commit(next, goal.status);
|
|
398
|
+
return next;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// inconclusive: evidence too weak either way; twice in a row = human call.
|
|
402
|
+
if (input.verifier.verdict === "inconclusive") {
|
|
403
|
+
inconclusiveStreak = inconclusiveStreak + 1;
|
|
404
|
+
if (inconclusiveStreak >= INCONCLUSIVE_STREAK_CAP) {
|
|
405
|
+
const next = this.withGoal(goal, {
|
|
406
|
+
status: "paused",
|
|
407
|
+
reason: "paused(verifier_inconclusive)",
|
|
408
|
+
inconclusiveStreak,
|
|
409
|
+
});
|
|
410
|
+
this.goal = next;
|
|
411
|
+
this.commit(next, goal.status);
|
|
412
|
+
return next;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
inconclusiveStreak = 0;
|
|
368
417
|
}
|
|
369
418
|
|
|
370
419
|
// 2. Safety refusal is immediately terminal.
|
|
@@ -457,9 +506,11 @@ export class GoalMachine {
|
|
|
457
506
|
tokensAtStart,
|
|
458
507
|
tokensNow,
|
|
459
508
|
tokensBaselinePending,
|
|
509
|
+
...(input.tokensEstimated !== undefined ? { tokensEstimated: input.tokensEstimated } : {}),
|
|
460
510
|
turn,
|
|
461
511
|
noProgressStreak,
|
|
462
512
|
notMetStreak,
|
|
513
|
+
inconclusiveStreak,
|
|
463
514
|
blockedProposalStreak,
|
|
464
515
|
});
|
|
465
516
|
this.goal = next;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-slot nudge stash — the long-horizon handoff between `agent_end`
|
|
3
|
+
* settlement and the arbiter's `agent_before_settle` boundary.
|
|
4
|
+
*
|
|
5
|
+
* Continuation/ralph "sends" land here instead of pi's message queue; the
|
|
6
|
+
* package's nudge provider (priority 100) returns the stash at the boundary
|
|
7
|
+
* and takes it on delivery. A put overwrites an undelivered nudge (the newest
|
|
8
|
+
* intent wins) EXCEPT when the undelivered stash is a kickoff: the kickoff
|
|
9
|
+
* contract must never be lost, so a new put appends with a blank line and the
|
|
10
|
+
* merged stash stays kickoff-flagged until delivered.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface NudgeStashPutOptions {
|
|
14
|
+
/** Marks the kickoff contract (append-only while undelivered). */
|
|
15
|
+
kickoff?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class NudgeStash {
|
|
19
|
+
private text: string | null = null;
|
|
20
|
+
private kickoff = false;
|
|
21
|
+
|
|
22
|
+
put(text: string, options: NudgeStashPutOptions = {}): void {
|
|
23
|
+
if (this.text !== null && this.kickoff) {
|
|
24
|
+
this.text = `${this.text}\n\n${text}`;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.text = text;
|
|
28
|
+
this.kickoff = options.kickoff ?? false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Undelivered text, or null. */
|
|
32
|
+
peek(): string | null {
|
|
33
|
+
return this.text;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Take the text out (delivered). */
|
|
37
|
+
take(): string | null {
|
|
38
|
+
const text = this.text;
|
|
39
|
+
this.text = null;
|
|
40
|
+
this.kickoff = false;
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Owner lifecycle events that invalidate an undelivered nudge: when the owner
|
|
47
|
+
* stops or parks, a queued hint targets a machine that no longer drives. The
|
|
48
|
+
* wrap-up survives because continuation puts it AFTER owner.finish commits.
|
|
49
|
+
*/
|
|
50
|
+
export interface StashMetaState<M> {
|
|
51
|
+
meta: M | undefined;
|
|
52
|
+
kickoff: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Pure metadata mirror of the stash merge semantics: a put into an undelivered
|
|
56
|
+
* KICKOFF appends and KEEPS the original provenance; any other put replaces it.
|
|
57
|
+
* `hasText` mirrors stash.peek() !== null. */
|
|
58
|
+
export function nextStashMetaState<M>(
|
|
59
|
+
prev: StashMetaState<M> | undefined,
|
|
60
|
+
hasText: boolean,
|
|
61
|
+
kind: "kickoff" | undefined,
|
|
62
|
+
incoming: M,
|
|
63
|
+
): StashMetaState<M> {
|
|
64
|
+
const merging = hasText && (prev?.kickoff ?? false);
|
|
65
|
+
if (merging) return { meta: prev?.meta, kickoff: true };
|
|
66
|
+
return { meta: incoming, kickoff: kind === "kickoff" };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function ownerEventClearsStash(event: { type: string }): boolean {
|
|
70
|
+
return event.type === "finished" || event.type === "suspended";
|
|
71
|
+
}
|
|
@@ -93,7 +93,8 @@ export interface RalphLoopDeps {
|
|
|
93
93
|
readonly owner: OwnerCoordinator;
|
|
94
94
|
/** Resolve the ralph dir (usually ctx.cwd-based). */
|
|
95
95
|
ralphDir(): string;
|
|
96
|
-
|
|
96
|
+
/** Lands in the arbiter's nudge stash; the first prompt is the kickoff contract. */
|
|
97
|
+
send(message: string, kind?: "kickoff"): void;
|
|
97
98
|
now?(): number;
|
|
98
99
|
/** Footer/info-screen events (loop_start / iteration_done / loop_end). */
|
|
99
100
|
onEvent?(event: RalphEvent): void;
|
|
@@ -214,7 +215,7 @@ export class RalphLoop {
|
|
|
214
215
|
const itemsTotal = parseChecklist(taskContent).length;
|
|
215
216
|
this.deps.onEvent?.({ type: "loop_start", name: state.name, iteration: 1, total: itemsTotal });
|
|
216
217
|
const firstPrompt = this.buildIterationPrompt(state, false);
|
|
217
|
-
this.deps.send(firstPrompt);
|
|
218
|
+
this.deps.send(firstPrompt, "kickoff");
|
|
218
219
|
return { ok: true, state, firstPrompt };
|
|
219
220
|
}
|
|
220
221
|
|