@fitchmultz/pi-subagents 0.0.0-stage → 0.44.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1654 -0
- package/LICENSE +21 -0
- package/README.md +1533 -2
- package/agents/context-builder.md +97 -0
- package/agents/debugger.md +51 -0
- package/agents/delegate.md +13 -0
- package/agents/fixer.md +37 -0
- package/agents/oracle.md +71 -0
- package/agents/planner.md +72 -0
- package/agents/researcher.md +51 -0
- package/agents/reviewer-claude.md +69 -0
- package/agents/reviewer-gpt.md +68 -0
- package/agents/reviewer-ponytail.md +82 -0
- package/agents/reviewer-security.md +81 -0
- package/agents/reviewer.md +69 -0
- package/agents/scout.md +66 -0
- package/agents/ui-designer.md +51 -0
- package/agents/watcher.md +26 -0
- package/agents/worker.md +46 -0
- package/agents/writer.md +36 -0
- package/dist/agents/agent-management.js +923 -0
- package/dist/agents/agent-scope.js +7 -0
- package/dist/agents/agent-selection.js +20 -0
- package/dist/agents/agent-serializer.js +100 -0
- package/dist/agents/agents.js +752 -0
- package/dist/agents/chain-serializer.js +367 -0
- package/dist/agents/frontmatter.js +21 -0
- package/dist/agents/identity.js +29 -0
- package/dist/agents/skills.js +570 -0
- package/dist/extension/build-info.js +1 -0
- package/dist/extension/compact-tools.js +112 -0
- package/dist/extension/config.js +22 -0
- package/dist/extension/control-notices.js +29 -0
- package/dist/extension/doctor.js +143 -0
- package/dist/extension/fanout-child.js +326 -0
- package/dist/extension/index.js +577 -0
- package/dist/extension/schemas.js +416 -0
- package/dist/extension/tool-input.js +20 -0
- package/dist/extension/tool-result.js +14 -0
- package/dist/history/canonical-result.js +81 -0
- package/dist/history/database.js +72 -0
- package/dist/history/index.js +199 -0
- package/dist/history/ingest.js +468 -0
- package/dist/history/queries.js +350 -0
- package/dist/history/store.js +216 -0
- package/dist/history/types.js +4 -0
- package/dist/history/worker.js +466 -0
- package/dist/intercom/intercom-bridge.js +112 -0
- package/dist/intercom/live-intercom.js +94 -0
- package/dist/intercom/result-intercom.js +373 -0
- package/dist/pi-intercom/agent-dir.js +10 -0
- package/dist/pi-intercom/bridge.js +750 -0
- package/dist/pi-intercom/broker/broker.js +426 -0
- package/dist/pi-intercom/broker/client.js +492 -0
- package/dist/pi-intercom/broker/framing.js +65 -0
- package/dist/pi-intercom/broker/paths.js +42 -0
- package/dist/pi-intercom/broker/pid.js +66 -0
- package/dist/pi-intercom/broker/spawn.js +221 -0
- package/dist/pi-intercom/config.js +102 -0
- package/dist/pi-intercom/index.js +2942 -0
- package/dist/pi-intercom/reply-tracker.js +182 -0
- package/dist/pi-intercom/session-targets.js +128 -0
- package/dist/pi-intercom/subagent-live-events.js +132 -0
- package/dist/pi-intercom/topics.js +157 -0
- package/dist/pi-intercom/types.js +163 -0
- package/dist/pi-intercom/ui/compose.js +269 -0
- package/dist/pi-intercom/ui/inline-message.js +80 -0
- package/dist/pi-intercom/ui/session-list.js +124 -0
- package/dist/runs/background/async-control.js +95 -0
- package/dist/runs/background/async-execution.js +676 -0
- package/dist/runs/background/async-job-tracker.js +383 -0
- package/dist/runs/background/async-result-file.js +58 -0
- package/dist/runs/background/async-resume.js +305 -0
- package/dist/runs/background/async-status.js +442 -0
- package/dist/runs/background/completion-dedupe.js +69 -0
- package/dist/runs/background/completion-delivery.js +245 -0
- package/dist/runs/background/notify.js +71 -0
- package/dist/runs/background/parallel-groups.js +55 -0
- package/dist/runs/background/result-watcher.js +426 -0
- package/dist/runs/background/run-id-resolver.js +64 -0
- package/dist/runs/background/run-status.js +382 -0
- package/dist/runs/background/stale-run-reconciler.js +335 -0
- package/dist/runs/background/subagent-runner-launcher.js +55 -0
- package/dist/runs/background/subagent-runner.js +1872 -0
- package/dist/runs/background/top-level-async.js +5 -0
- package/dist/runs/foreground/chain-clarify.js +1152 -0
- package/dist/runs/foreground/clarify-invocation.js +72 -0
- package/dist/runs/foreground/execution-input.js +423 -0
- package/dist/runs/foreground/foreground-control.js +817 -0
- package/dist/runs/foreground/question-control.js +88 -0
- package/dist/runs/foreground/run-async-path.js +205 -0
- package/dist/runs/foreground/subagent-executor.js +442 -0
- package/dist/runs/foreground/subagent-params.js +165 -0
- package/dist/runs/foreground/wait-run.js +141 -0
- package/dist/runs/shared/acceptance-contract.js +253 -0
- package/dist/runs/shared/acceptance-evaluation.js +256 -0
- package/dist/runs/shared/acceptance-finalization.js +280 -0
- package/dist/runs/shared/acceptance-reports.js +147 -0
- package/dist/runs/shared/acceptance.js +4 -0
- package/dist/runs/shared/chain-outputs.js +110 -0
- package/dist/runs/shared/child-attempt.js +693 -0
- package/dist/runs/shared/child-execution-cwd.js +96 -0
- package/dist/runs/shared/child-observations.js +114 -0
- package/dist/runs/shared/claude-code.js +199 -0
- package/dist/runs/shared/completion-guard.js +31 -0
- package/dist/runs/shared/dynamic-fanout.js +274 -0
- package/dist/runs/shared/history-index.js +72 -0
- package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
- package/dist/runs/shared/model-fallback.js +185 -0
- package/dist/runs/shared/mutating-tool-guard.js +170 -0
- package/dist/runs/shared/native-finalization.js +101 -0
- package/dist/runs/shared/native-usage.js +153 -0
- package/dist/runs/shared/nested-events.js +700 -0
- package/dist/runs/shared/nested-path.js +48 -0
- package/dist/runs/shared/nested-render.js +108 -0
- package/dist/runs/shared/parallel-utils.js +61 -0
- package/dist/runs/shared/parent-receipts.js +68 -0
- package/dist/runs/shared/parent-usage.js +121 -0
- package/dist/runs/shared/pi-args.js +310 -0
- package/dist/runs/shared/pi-spawn.js +52 -0
- package/dist/runs/shared/run-history-queries.js +45 -0
- package/dist/runs/shared/run-history.js +110 -0
- package/dist/runs/shared/run-id-ambiguity.js +4 -0
- package/dist/runs/shared/run-records.js +679 -0
- package/dist/runs/shared/session-cwd-preload.js +33 -0
- package/dist/runs/shared/single-output.js +222 -0
- package/dist/runs/shared/streaming-text.js +15 -0
- package/dist/runs/shared/structured-output.js +55 -0
- package/dist/runs/shared/subagent-control.js +179 -0
- package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
- package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
- package/dist/runs/shared/supervisor-questions.js +350 -0
- package/dist/runs/shared/workflow-graph.js +214 -0
- package/dist/runs/shared/workflow-policy.js +84 -0
- package/dist/runs/shared/worktree.js +525 -0
- package/dist/shared/agent-context-policy.js +126 -0
- package/dist/shared/artifacts.js +96 -0
- package/dist/shared/atomic-json.js +13 -0
- package/dist/shared/cooperative.js +20 -0
- package/dist/shared/execution-cwd.js +14 -0
- package/dist/shared/file-coalescer.js +21 -0
- package/dist/shared/fork-context.js +49 -0
- package/dist/shared/formatters.js +85 -0
- package/dist/shared/journal-reader.js +407 -0
- package/dist/shared/lazy-tools.js +32 -0
- package/dist/shared/model-info.js +68 -0
- package/dist/shared/native-session.js +15 -0
- package/dist/shared/native-tui.js +18 -0
- package/dist/shared/native-typebox.js +20 -0
- package/dist/shared/post-exit-stdio-guard.js +197 -0
- package/dist/shared/prompt-sections.js +8 -0
- package/dist/shared/session-entries.js +52 -0
- package/dist/shared/session-identity.js +14 -0
- package/dist/shared/settings.js +254 -0
- package/dist/shared/status-format.js +111 -0
- package/dist/shared/temp-root.js +114 -0
- package/dist/shared/types.js +207 -0
- package/dist/shared/ui-mode.js +3 -0
- package/dist/shared/utils.js +334 -0
- package/dist/slash/prompt-template-bridge.js +298 -0
- package/dist/slash/slash-bridge.js +116 -0
- package/dist/slash/slash-commands.js +607 -0
- package/dist/slash/slash-live-state.js +246 -0
- package/dist/tui/action-hints.js +75 -0
- package/dist/tui/agent-history.js +197 -0
- package/dist/tui/agent-view.js +1656 -0
- package/dist/tui/render.js +1515 -0
- package/docs/intercom-bridge.md +212 -0
- package/docs/intercom.md +581 -0
- package/docs/performance.md +121 -0
- package/package.json +102 -4
- package/prompts/gather-context-and-clarify.md +14 -0
- package/prompts/parallel-cleanup.md +62 -0
- package/prompts/parallel-context-build.md +56 -0
- package/prompts/parallel-handoff-plan.md +62 -0
- package/prompts/parallel-research.md +51 -0
- package/prompts/parallel-review.md +59 -0
- package/prompts/review-loop.md +42 -0
- package/scripts/build.mjs +134 -0
- package/scripts/check-compat.mjs +48 -0
- package/scripts/compat-host.mjs +19 -0
- package/scripts/intercom-bridge-credentials.mjs +143 -0
- package/scripts/intercom-remote.mjs +103 -0
- package/scripts/linux-smoke.sh +51 -0
- package/scripts/local-install-smoke.mjs +66 -0
- package/scripts/native-package-smoke.mjs +82 -0
- package/scripts/package-smoke.mjs +209 -0
- package/scripts/prepare.mjs +67 -0
- package/scripts/real-pi-smoke.mjs +415 -0
- package/scripts/run-tests.mjs +149 -0
- package/skills/pi-intercom/SKILL.md +113 -0
- package/skills/pi-intercom/evals/evals.json +29 -0
- package/skills/pi-intercom/evals/trigger-evals.json +47 -0
- package/skills/pi-intercom/references/peer-sessions.md +69 -0
- package/skills/pi-subagents/SKILL.md +76 -0
- package/skills/pi-subagents/evals/evals.json +29 -0
- package/skills/pi-subagents/evals/trigger-evals.json +42 -0
- package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
- package/src/agents/agent-management.ts +813 -0
- package/src/agents/agent-scope.ts +7 -0
- package/src/agents/agent-selection.ts +23 -0
- package/src/agents/agent-serializer.ts +99 -0
- package/src/agents/agents.ts +916 -0
- package/src/agents/chain-serializer.ts +329 -0
- package/src/agents/frontmatter.ts +21 -0
- package/src/agents/identity.ts +30 -0
- package/src/agents/skills.ts +637 -0
- package/src/extension/build-info.ts +2 -0
- package/src/extension/compact-tools.ts +114 -0
- package/src/extension/config.ts +23 -0
- package/src/extension/control-notices.ts +47 -0
- package/src/extension/doctor.ts +171 -0
- package/src/extension/fanout-child.ts +312 -0
- package/src/extension/index.ts +649 -0
- package/src/extension/schemas.ts +444 -0
- package/src/extension/tool-input.ts +19 -0
- package/src/extension/tool-result.ts +16 -0
- package/src/history/canonical-result.ts +62 -0
- package/src/history/database.ts +59 -0
- package/src/history/index.ts +156 -0
- package/src/history/ingest.ts +349 -0
- package/src/history/queries.ts +241 -0
- package/src/history/store.ts +203 -0
- package/src/history/types.ts +166 -0
- package/src/history/worker.ts +321 -0
- package/src/intercom/intercom-bridge.ts +123 -0
- package/src/intercom/live-intercom.ts +104 -0
- package/src/intercom/result-intercom.ts +429 -0
- package/src/pi-intercom/agent-dir.ts +9 -0
- package/src/pi-intercom/bridge.ts +668 -0
- package/src/pi-intercom/broker/broker.ts +463 -0
- package/src/pi-intercom/broker/client.ts +558 -0
- package/src/pi-intercom/broker/framing.ts +77 -0
- package/src/pi-intercom/broker/paths.ts +47 -0
- package/src/pi-intercom/broker/pid.ts +59 -0
- package/src/pi-intercom/broker/spawn.ts +248 -0
- package/src/pi-intercom/config.ts +142 -0
- package/src/pi-intercom/index.ts +2929 -0
- package/src/pi-intercom/reply-tracker.ts +211 -0
- package/src/pi-intercom/session-targets.ts +172 -0
- package/src/pi-intercom/subagent-live-events.ts +133 -0
- package/src/pi-intercom/topics.ts +125 -0
- package/src/pi-intercom/types.ts +271 -0
- package/src/pi-intercom/ui/compose.ts +289 -0
- package/src/pi-intercom/ui/inline-message.ts +92 -0
- package/src/pi-intercom/ui/session-list.ts +139 -0
- package/src/runs/background/async-control.ts +89 -0
- package/src/runs/background/async-execution.ts +850 -0
- package/src/runs/background/async-job-tracker.ts +385 -0
- package/src/runs/background/async-result-file.ts +62 -0
- package/src/runs/background/async-resume.ts +333 -0
- package/src/runs/background/async-status.ts +481 -0
- package/src/runs/background/completion-dedupe.ts +79 -0
- package/src/runs/background/completion-delivery.ts +195 -0
- package/src/runs/background/notify.ts +123 -0
- package/src/runs/background/parallel-groups.ts +53 -0
- package/src/runs/background/result-watcher.ts +423 -0
- package/src/runs/background/run-id-resolver.ts +69 -0
- package/src/runs/background/run-status.ts +384 -0
- package/src/runs/background/stale-run-reconciler.ts +369 -0
- package/src/runs/background/subagent-runner-launcher.ts +56 -0
- package/src/runs/background/subagent-runner.ts +2053 -0
- package/src/runs/background/top-level-async.ts +13 -0
- package/src/runs/foreground/chain-clarify.ts +1340 -0
- package/src/runs/foreground/clarify-invocation.ts +77 -0
- package/src/runs/foreground/execution-input.ts +467 -0
- package/src/runs/foreground/foreground-control.ts +821 -0
- package/src/runs/foreground/question-control.ts +82 -0
- package/src/runs/foreground/run-async-path.ts +243 -0
- package/src/runs/foreground/subagent-executor.ts +501 -0
- package/src/runs/foreground/subagent-params.ts +320 -0
- package/src/runs/foreground/wait-run.ts +124 -0
- package/src/runs/shared/acceptance-contract.ts +257 -0
- package/src/runs/shared/acceptance-evaluation.ts +275 -0
- package/src/runs/shared/acceptance-finalization.ts +353 -0
- package/src/runs/shared/acceptance-reports.ts +152 -0
- package/src/runs/shared/acceptance.ts +33 -0
- package/src/runs/shared/chain-outputs.ts +114 -0
- package/src/runs/shared/child-attempt.ts +623 -0
- package/src/runs/shared/child-execution-cwd.ts +91 -0
- package/src/runs/shared/child-observations.ts +99 -0
- package/src/runs/shared/claude-code.ts +243 -0
- package/src/runs/shared/completion-guard.ts +35 -0
- package/src/runs/shared/dynamic-fanout.ts +295 -0
- package/src/runs/shared/history-index.ts +69 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
- package/src/runs/shared/model-fallback.ts +219 -0
- package/src/runs/shared/mutating-tool-guard.ts +194 -0
- package/src/runs/shared/native-finalization.ts +128 -0
- package/src/runs/shared/native-usage.ts +113 -0
- package/src/runs/shared/nested-events.ts +752 -0
- package/src/runs/shared/nested-path.ts +52 -0
- package/src/runs/shared/nested-render.ts +119 -0
- package/src/runs/shared/parallel-utils.ts +153 -0
- package/src/runs/shared/parent-receipts.ts +59 -0
- package/src/runs/shared/parent-usage.ts +121 -0
- package/src/runs/shared/pi-args.ts +350 -0
- package/src/runs/shared/pi-spawn.ts +52 -0
- package/src/runs/shared/run-history-queries.ts +44 -0
- package/src/runs/shared/run-history.ts +100 -0
- package/src/runs/shared/run-id-ambiguity.ts +4 -0
- package/src/runs/shared/run-records.ts +607 -0
- package/src/runs/shared/session-cwd-preload.ts +27 -0
- package/src/runs/shared/single-output.ts +267 -0
- package/src/runs/shared/streaming-text.ts +18 -0
- package/src/runs/shared/structured-output.ts +68 -0
- package/src/runs/shared/subagent-control.ts +219 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
- package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
- package/src/runs/shared/supervisor-questions.ts +415 -0
- package/src/runs/shared/workflow-graph.ts +220 -0
- package/src/runs/shared/workflow-policy.ts +125 -0
- package/src/runs/shared/worktree.ts +635 -0
- package/src/shared/agent-context-policy.ts +184 -0
- package/src/shared/artifacts.ts +95 -0
- package/src/shared/atomic-json.ts +16 -0
- package/src/shared/cooperative.ts +21 -0
- package/src/shared/execution-cwd.ts +14 -0
- package/src/shared/file-coalescer.ts +29 -0
- package/src/shared/fork-context.ts +68 -0
- package/src/shared/formatters.ts +84 -0
- package/src/shared/journal-reader.ts +333 -0
- package/src/shared/lazy-tools.ts +36 -0
- package/src/shared/model-info.ts +81 -0
- package/src/shared/native-session.ts +8 -0
- package/src/shared/native-tui.ts +10 -0
- package/src/shared/native-typebox.ts +14 -0
- package/src/shared/post-exit-stdio-guard.ts +204 -0
- package/src/shared/prompt-sections.ts +10 -0
- package/src/shared/session-entries.ts +49 -0
- package/src/shared/session-identity.ts +18 -0
- package/src/shared/settings.ts +384 -0
- package/src/shared/status-format.ts +123 -0
- package/src/shared/temp-root.ts +107 -0
- package/src/shared/types.ts +1260 -0
- package/src/shared/ui-mode.ts +5 -0
- package/src/shared/utils.ts +342 -0
- package/src/slash/prompt-template-bridge.ts +408 -0
- package/src/slash/slash-bridge.ts +178 -0
- package/src/slash/slash-commands.ts +617 -0
- package/src/slash/slash-live-state.ts +292 -0
- package/src/tui/action-hints.ts +69 -0
- package/src/tui/agent-history.ts +210 -0
- package/src/tui/agent-view.ts +1323 -0
- package/src/tui/render.ts +1550 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { SLASH_RESULT_TYPE } from "../shared/types.js";
|
|
2
|
+
const liveSnapshots = new Map();
|
|
3
|
+
const finalSnapshots = new Map();
|
|
4
|
+
let versionCounter = 1;
|
|
5
|
+
const EMPTY_MESSAGES = [];
|
|
6
|
+
const EMPTY_USAGE = {
|
|
7
|
+
input: 0,
|
|
8
|
+
output: 0,
|
|
9
|
+
cacheRead: 0,
|
|
10
|
+
cacheWrite: 0,
|
|
11
|
+
cost: 0,
|
|
12
|
+
turns: 0,
|
|
13
|
+
};
|
|
14
|
+
function nextVersion() {
|
|
15
|
+
return versionCounter++;
|
|
16
|
+
}
|
|
17
|
+
function cloneUsage() {
|
|
18
|
+
return { ...EMPTY_USAGE };
|
|
19
|
+
}
|
|
20
|
+
function createPlaceholderResult(agent, task, status, index = 0) {
|
|
21
|
+
return {
|
|
22
|
+
agent,
|
|
23
|
+
task,
|
|
24
|
+
exitCode: 0,
|
|
25
|
+
messages: EMPTY_MESSAGES,
|
|
26
|
+
usage: cloneUsage(),
|
|
27
|
+
progress: {
|
|
28
|
+
index,
|
|
29
|
+
agent,
|
|
30
|
+
status,
|
|
31
|
+
task,
|
|
32
|
+
recentTools: [],
|
|
33
|
+
recentOutput: [],
|
|
34
|
+
toolCount: 0,
|
|
35
|
+
tokens: 0,
|
|
36
|
+
durationMs: 0,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function buildParallelInitialResult(params) {
|
|
41
|
+
const tasks = params.tasks ?? [];
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: "text", text: tasks.map((task) => `${task.agent}: ${task.task}`).join("\n\n") }],
|
|
44
|
+
details: {
|
|
45
|
+
mode: "parallel",
|
|
46
|
+
...(params.context ? { context: params.context } : {}),
|
|
47
|
+
results: tasks.map((task, index) => createPlaceholderResult(task.agent, task.task, "running", index)),
|
|
48
|
+
progress: tasks.map((task, index) => ({
|
|
49
|
+
index,
|
|
50
|
+
agent: task.agent,
|
|
51
|
+
status: "running",
|
|
52
|
+
task: task.task,
|
|
53
|
+
recentTools: [],
|
|
54
|
+
recentOutput: [],
|
|
55
|
+
toolCount: 0,
|
|
56
|
+
tokens: 0,
|
|
57
|
+
durationMs: 0,
|
|
58
|
+
})),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function isParallelChainStep(step) {
|
|
63
|
+
return "parallel" in step && Array.isArray(step.parallel);
|
|
64
|
+
}
|
|
65
|
+
function chainStepLabel(step) {
|
|
66
|
+
if (isParallelChainStep(step)) {
|
|
67
|
+
return `[${step.parallel.map((entry) => entry.agent).join("+")}]`;
|
|
68
|
+
}
|
|
69
|
+
return step.agent;
|
|
70
|
+
}
|
|
71
|
+
function flattenChainResults(chain, fallbackTask) {
|
|
72
|
+
const results = [];
|
|
73
|
+
let flatIndex = 0;
|
|
74
|
+
for (const step of chain) {
|
|
75
|
+
if (isParallelChainStep(step)) {
|
|
76
|
+
for (const task of step.parallel) {
|
|
77
|
+
results.push(createPlaceholderResult(task.agent, task.task ?? fallbackTask ?? "", results.length === 0 ? "running" : "pending", flatIndex));
|
|
78
|
+
flatIndex++;
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
results.push(createPlaceholderResult(step.agent, step.task ?? fallbackTask ?? "", results.length === 0 ? "running" : "pending", flatIndex));
|
|
83
|
+
flatIndex++;
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
function buildChainInitialResult(params) {
|
|
88
|
+
const chain = (params.chain ?? []);
|
|
89
|
+
const results = flattenChainResults(chain, params.task);
|
|
90
|
+
return {
|
|
91
|
+
content: [{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: results.map((result, index) => `Step ${index + 1}: ${result.agent}\n${result.task}`).join("\n\n"),
|
|
94
|
+
}],
|
|
95
|
+
details: {
|
|
96
|
+
mode: "chain",
|
|
97
|
+
...(params.context ? { context: params.context } : {}),
|
|
98
|
+
results,
|
|
99
|
+
progress: results.map((result, index) => ({
|
|
100
|
+
index,
|
|
101
|
+
agent: result.agent,
|
|
102
|
+
status: index === 0 ? "running" : "pending",
|
|
103
|
+
task: result.task,
|
|
104
|
+
recentTools: [],
|
|
105
|
+
recentOutput: [],
|
|
106
|
+
toolCount: 0,
|
|
107
|
+
tokens: 0,
|
|
108
|
+
durationMs: 0,
|
|
109
|
+
})),
|
|
110
|
+
chainAgents: chain.map((step) => chainStepLabel(step)),
|
|
111
|
+
totalSteps: chain.length,
|
|
112
|
+
currentStepIndex: 0,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function buildSingleInitialResult(params) {
|
|
117
|
+
const agent = params.agent ?? "subagent";
|
|
118
|
+
const task = params.task ?? "";
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text", text: task }],
|
|
121
|
+
details: {
|
|
122
|
+
mode: "single",
|
|
123
|
+
...(params.context ? { context: params.context } : {}),
|
|
124
|
+
results: [createPlaceholderResult(agent, task, "running")],
|
|
125
|
+
progress: [{
|
|
126
|
+
index: 0,
|
|
127
|
+
agent,
|
|
128
|
+
status: "running",
|
|
129
|
+
task,
|
|
130
|
+
recentTools: [],
|
|
131
|
+
recentOutput: [],
|
|
132
|
+
toolCount: 0,
|
|
133
|
+
tokens: 0,
|
|
134
|
+
durationMs: 0,
|
|
135
|
+
}],
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export function buildSlashInitialResult(requestId, params) {
|
|
140
|
+
const result = (params.tasks?.length ?? 0) > 0
|
|
141
|
+
? buildParallelInitialResult(params)
|
|
142
|
+
: (params.chain?.length ?? 0) > 0
|
|
143
|
+
? buildChainInitialResult(params)
|
|
144
|
+
: buildSingleInitialResult(params);
|
|
145
|
+
liveSnapshots.set(requestId, { result, version: nextVersion() });
|
|
146
|
+
finalSnapshots.delete(requestId);
|
|
147
|
+
return { requestId, result };
|
|
148
|
+
}
|
|
149
|
+
function cloneResultsWithProgress(results, progress) {
|
|
150
|
+
return results.map((result, index) => {
|
|
151
|
+
const nextProgress = progress?.find((entry) => entry.index === index)
|
|
152
|
+
?? progress?.[index]
|
|
153
|
+
?? result.progress;
|
|
154
|
+
return nextProgress ? { ...result, progress: nextProgress } : result;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
export function applySlashUpdate(requestId, update) {
|
|
158
|
+
const snapshot = liveSnapshots.get(requestId);
|
|
159
|
+
if (!snapshot)
|
|
160
|
+
return;
|
|
161
|
+
const progress = update.progress;
|
|
162
|
+
if (!progress || !snapshot.result.details)
|
|
163
|
+
return;
|
|
164
|
+
const currentStepIndex = progress.findIndex((entry) => entry.status === "running");
|
|
165
|
+
const nextDetails = {
|
|
166
|
+
...snapshot.result.details,
|
|
167
|
+
progress,
|
|
168
|
+
results: cloneResultsWithProgress(snapshot.result.details.results, progress),
|
|
169
|
+
...(snapshot.result.details.mode === "chain" && currentStepIndex >= 0 ? { currentStepIndex } : {}),
|
|
170
|
+
};
|
|
171
|
+
liveSnapshots.set(requestId, {
|
|
172
|
+
result: {
|
|
173
|
+
...snapshot.result,
|
|
174
|
+
details: nextDetails,
|
|
175
|
+
},
|
|
176
|
+
version: nextVersion(),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
export function finalizeSlashResult(response) {
|
|
180
|
+
const snapshot = {
|
|
181
|
+
result: response.result,
|
|
182
|
+
version: nextVersion(),
|
|
183
|
+
};
|
|
184
|
+
finalSnapshots.set(response.requestId, snapshot);
|
|
185
|
+
liveSnapshots.delete(response.requestId);
|
|
186
|
+
return {
|
|
187
|
+
requestId: response.requestId,
|
|
188
|
+
result: response.result,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export function failSlashResult(requestId, params, message) {
|
|
192
|
+
const initial = buildSlashInitialResult(requestId, params).result;
|
|
193
|
+
const failedResults = initial.details.results.map((result) => ({
|
|
194
|
+
...result,
|
|
195
|
+
exitCode: 1,
|
|
196
|
+
error: message,
|
|
197
|
+
progress: result.progress ? { ...result.progress, status: "failed" } : result.progress,
|
|
198
|
+
}));
|
|
199
|
+
const result = {
|
|
200
|
+
content: [{ type: "text", text: message }],
|
|
201
|
+
details: {
|
|
202
|
+
...initial.details,
|
|
203
|
+
results: failedResults,
|
|
204
|
+
progress: failedResults.map((entry) => entry.progress).filter(Boolean),
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
const snapshot = { result, version: nextVersion() };
|
|
208
|
+
finalSnapshots.set(requestId, snapshot);
|
|
209
|
+
liveSnapshots.delete(requestId);
|
|
210
|
+
return { requestId, result };
|
|
211
|
+
}
|
|
212
|
+
function isSlashMessageDetails(value) {
|
|
213
|
+
if (!value || typeof value !== "object")
|
|
214
|
+
return false;
|
|
215
|
+
const v = value;
|
|
216
|
+
if (typeof v.requestId !== "string" || !v.requestId)
|
|
217
|
+
return false;
|
|
218
|
+
if (!v.result || !Array.isArray(v.result.content))
|
|
219
|
+
return false;
|
|
220
|
+
return !!v.result.details && Array.isArray(v.result.details.results);
|
|
221
|
+
}
|
|
222
|
+
export function resolveSlashMessageDetails(value) {
|
|
223
|
+
return isSlashMessageDetails(value) ? value : undefined;
|
|
224
|
+
}
|
|
225
|
+
export function getSlashRenderableSnapshot(details) {
|
|
226
|
+
return finalSnapshots.get(details.requestId)
|
|
227
|
+
?? liveSnapshots.get(details.requestId)
|
|
228
|
+
?? { result: details.result, version: 0 };
|
|
229
|
+
}
|
|
230
|
+
export function restoreSlashFinalSnapshots(entries) {
|
|
231
|
+
liveSnapshots.clear();
|
|
232
|
+
finalSnapshots.clear();
|
|
233
|
+
for (const entry of entries) {
|
|
234
|
+
const e = entry;
|
|
235
|
+
if (e?.type !== "custom_message" || e.customType !== SLASH_RESULT_TYPE)
|
|
236
|
+
continue;
|
|
237
|
+
const details = resolveSlashMessageDetails(e.details);
|
|
238
|
+
if (!details)
|
|
239
|
+
continue;
|
|
240
|
+
finalSnapshots.set(details.requestId, { result: details.result, version: nextVersion() });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
export function clearSlashSnapshots() {
|
|
244
|
+
liveSnapshots.clear();
|
|
245
|
+
finalSnapshots.clear();
|
|
246
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { rawKeyHint } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { MouseRegion, Text, stripTerminalSequences, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
/** Native custom messages expose global expansion, but need their own per-message click state. */
|
|
4
|
+
export function withMouseExpansion(renderer) {
|
|
5
|
+
const states = new WeakMap();
|
|
6
|
+
return (message, options, theme) => {
|
|
7
|
+
let state = states.get(message);
|
|
8
|
+
if (!state || state.globalExpanded !== options.expanded) {
|
|
9
|
+
state = { expanded: options.expanded, globalExpanded: options.expanded };
|
|
10
|
+
states.set(message, state);
|
|
11
|
+
}
|
|
12
|
+
const expansion = state;
|
|
13
|
+
const draw = () => renderer(message, { ...options, expanded: expansion.expanded }, theme);
|
|
14
|
+
let content = draw();
|
|
15
|
+
if (!content)
|
|
16
|
+
return;
|
|
17
|
+
return new MouseRegion({ render: (width) => content?.render(width) ?? [], invalidate: () => content?.invalidate() }, (event) => {
|
|
18
|
+
if (event.type !== "click" || event.button !== "left")
|
|
19
|
+
return;
|
|
20
|
+
expansion.expanded = !expansion.expanded;
|
|
21
|
+
content = draw();
|
|
22
|
+
return { handled: true };
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Declare action spans while building text; native Text owns wrapping and clipping. */
|
|
27
|
+
export function actionHints(parts, style = (text) => text, ellipsis) {
|
|
28
|
+
let text = "", source = "";
|
|
29
|
+
const ranges = [];
|
|
30
|
+
for (const part of parts) {
|
|
31
|
+
const displayed = typeof part === "string" ? part : part.text.replace(/\balt\+/gi, (key) => stripTerminalSequences(rawKeyHint(key, "")).trim());
|
|
32
|
+
const plain = stripTerminalSequences(displayed).replace(/\t/g, " ");
|
|
33
|
+
if (typeof part !== "string")
|
|
34
|
+
ranges.push({ start: source.length, length: plain.length, run: part.run });
|
|
35
|
+
text += displayed;
|
|
36
|
+
source += plain;
|
|
37
|
+
}
|
|
38
|
+
text = style(text);
|
|
39
|
+
const content = new Text("", 0, 0);
|
|
40
|
+
let hits = [];
|
|
41
|
+
return new MouseRegion({
|
|
42
|
+
invalidate() { content.invalidate(); hits = []; },
|
|
43
|
+
render(width) {
|
|
44
|
+
const clipped = ellipsis !== undefined && visibleWidth(text) > width;
|
|
45
|
+
content.setText(ellipsis === undefined ? text : truncateToWidth(text, width, ellipsis));
|
|
46
|
+
const lines = content.render(width);
|
|
47
|
+
hits = [];
|
|
48
|
+
let offset = 0;
|
|
49
|
+
for (const [row, line] of lines.entries()) {
|
|
50
|
+
const visible = stripTerminalSequences(line).trimEnd();
|
|
51
|
+
const shown = clipped ? visible.slice(0, Math.max(0, visible.length - ellipsis.length)) : visible;
|
|
52
|
+
const from = source.indexOf(shown, offset);
|
|
53
|
+
if (!shown || from < 0)
|
|
54
|
+
continue;
|
|
55
|
+
const to = from + shown.length;
|
|
56
|
+
for (const range of ranges) {
|
|
57
|
+
const start = Math.max(from, range.start), end = Math.min(to, range.start + range.length);
|
|
58
|
+
if (start < end)
|
|
59
|
+
hits.push({ row, start: visibleWidth(shown.slice(0, start - from)), end: visibleWidth(shown.slice(0, end - from)), run: range.run });
|
|
60
|
+
}
|
|
61
|
+
offset = to;
|
|
62
|
+
}
|
|
63
|
+
return lines;
|
|
64
|
+
},
|
|
65
|
+
}, (event) => {
|
|
66
|
+
if (event.button !== "left" || (event.type !== "press" && event.type !== "click"))
|
|
67
|
+
return;
|
|
68
|
+
const hit = hits.find((hit) => hit.row === event.y && event.x >= hit.start && event.x < hit.end);
|
|
69
|
+
if (!hit)
|
|
70
|
+
return;
|
|
71
|
+
if (event.type === "click")
|
|
72
|
+
hit.run();
|
|
73
|
+
return { handled: true };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
2
|
+
import { stripAcceptanceReport } from "../runs/shared/acceptance-reports.js";
|
|
3
|
+
import { extractToolArgsPreview } from "../shared/utils.js";
|
|
4
|
+
export function readableText(value) {
|
|
5
|
+
return stripTerminalSequences(typeof value === "string" ? value : JSON.stringify(value, null, 2) ?? "");
|
|
6
|
+
}
|
|
7
|
+
function contentText(content) {
|
|
8
|
+
if (typeof content === "string")
|
|
9
|
+
return readableText(content);
|
|
10
|
+
if (!Array.isArray(content))
|
|
11
|
+
return readableText(content);
|
|
12
|
+
return content.map((part) => part.type === "text" ? readableText(part.text)
|
|
13
|
+
: part.type === "thinking" ? readableText(part.thinking)
|
|
14
|
+
: part.type === "image" ? `[Image: ${part.mimeType ?? "image"}]` : "").filter(Boolean).join("\n");
|
|
15
|
+
}
|
|
16
|
+
function resultDisplay(result, call) {
|
|
17
|
+
const details = result.details;
|
|
18
|
+
return { text: contentText(result.content), diff: typeof details?.diff === "string" ? readableText(details.diff) : undefined,
|
|
19
|
+
details: readableText({ ...(call ? { call } : {}), result }) };
|
|
20
|
+
}
|
|
21
|
+
/** Keep reading/delivery facts eager; format bodies and raw tool details only when displayed. */
|
|
22
|
+
function historyItem(facts, format) {
|
|
23
|
+
let display;
|
|
24
|
+
return {
|
|
25
|
+
...facts,
|
|
26
|
+
get title() { return (display ??= format()).title; },
|
|
27
|
+
get text() { return (display ??= format()).text; },
|
|
28
|
+
get details() { return (display ??= format()).details; },
|
|
29
|
+
get diff() { return (display ??= format()).diff; },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Read the native entries, not a compacted model context: earlier messages remain inspectable. */
|
|
33
|
+
export function historyItems(entries) {
|
|
34
|
+
const items = [], entryIds = [];
|
|
35
|
+
const calls = new Map();
|
|
36
|
+
const append = (item) => { items.push(item); entryIds.push(item.id); };
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const base = { id: entry.id, timestamp: Date.parse(entry.timestamp) };
|
|
39
|
+
if (entry.type === "custom_message") {
|
|
40
|
+
const details = entry.details;
|
|
41
|
+
const human = entry.customType === "subagent-human-message";
|
|
42
|
+
append(historyItem({ ...base, kind: human ? "user" : "notice", ...(human ? { messageId: details?.message?.id } : {}) }, () => ({ title: human ? "User · delivered to conversation" : details?.from?.name ? `From ${details.from.name}` : entry.customType, text: contentText(details?.bodyText ?? entry.content) })));
|
|
43
|
+
}
|
|
44
|
+
else if (entry.type === "compaction") {
|
|
45
|
+
append(historyItem({ ...base, kind: "notice" }, () => ({ title: "Context summary · earlier history retained above", text: readableText(entry.summary) })));
|
|
46
|
+
}
|
|
47
|
+
else if (entry.type === "branch_summary") {
|
|
48
|
+
append(historyItem({ ...base, kind: "notice" }, () => ({ title: "Branch summary", text: readableText(entry.summary) })));
|
|
49
|
+
}
|
|
50
|
+
else if (entry.type === "message") {
|
|
51
|
+
const message = entry.message;
|
|
52
|
+
if (message.role === "assistant") {
|
|
53
|
+
const model = message.model ? (message.provider ? `${message.provider}/${message.model}` : message.model) : undefined;
|
|
54
|
+
const messageIds = message.content.flatMap((part, index) => part.type === "text" && part.text || part.type === "thinking" && part.thinking ? [`${entry.id}:${index}`] : []);
|
|
55
|
+
if (messageIds.length)
|
|
56
|
+
items.push(historyItem({ ...base, id: messageIds[0], entryIds: messageIds,
|
|
57
|
+
kind: message.content.some((part) => part.type === "text" && part.text) ? "assistant" : "thinking",
|
|
58
|
+
assistant: message, model }, () => ({ title: "Agent", text: contentText(message.content) })));
|
|
59
|
+
for (const [index, part] of message.content.entries()) {
|
|
60
|
+
const id = `${entry.id}:${index}`;
|
|
61
|
+
if (part.type === "toolCall") {
|
|
62
|
+
const item = historyItem({ ...base, id, entryIds: [id], kind: "tool", call: part, model }, () => ({
|
|
63
|
+
title: `${part.name} ${extractToolArgsPreview(part.arguments ?? {})}`.trim() + (item.result ? ` · ${item.result.isError ? "failed" : "result recorded"}` : " · result not recorded"),
|
|
64
|
+
...(item.result ? resultDisplay(item.result, part) : {
|
|
65
|
+
text: readableText(part.arguments),
|
|
66
|
+
details: `${readableText(part)}\n\nCommand result not recorded; exit is unconfirmed. An agent pause or exit does not prove that a command or its descendants exited.`,
|
|
67
|
+
}),
|
|
68
|
+
}));
|
|
69
|
+
append(item);
|
|
70
|
+
calls.set(part.id, item);
|
|
71
|
+
}
|
|
72
|
+
else if (messageIds.includes(id))
|
|
73
|
+
entryIds.push(id);
|
|
74
|
+
}
|
|
75
|
+
if (message.errorMessage) {
|
|
76
|
+
const id = `${entry.id}:error`;
|
|
77
|
+
if (messageIds.length && !message.content.some((part) => part.type === "toolCall") && ["error", "aborted"].includes(message.stopReason)) {
|
|
78
|
+
messageIds.push(id);
|
|
79
|
+
entryIds.push(id);
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
append(historyItem({ ...base, id, kind: "notice", model }, () => ({ title: "Agent error", text: readableText(message.errorMessage) })));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (message.role === "user") {
|
|
86
|
+
append(historyItem({ ...base, kind: "user" }, () => ({ title: "User / assignment", text: contentText(message.content) })));
|
|
87
|
+
}
|
|
88
|
+
else if (message.role === "toolResult") {
|
|
89
|
+
const call = calls.get(message.toolCallId);
|
|
90
|
+
if (call) {
|
|
91
|
+
call.result = message;
|
|
92
|
+
call.entryIds.push(entry.id);
|
|
93
|
+
entryIds.push(entry.id);
|
|
94
|
+
}
|
|
95
|
+
else
|
|
96
|
+
append(historyItem({ ...base, result: message, kind: "result" }, () => ({
|
|
97
|
+
title: `${message.toolName} · call not recorded`, ...resultDisplay(message),
|
|
98
|
+
})));
|
|
99
|
+
}
|
|
100
|
+
else if (message.role === "bashExecution") {
|
|
101
|
+
append(historyItem({ ...base, kind: "tool" }, () => ({ title: `Shell: ${readableText(message.command)}`, text: `${readableText(message.output)}\n${typeof message.exitCode === "number" ? `Shell exit code: ${message.exitCode}` : "Shell exit code not recorded"}${message.cancelled ? " · cancelled" : ""}`, details: readableText({ command: message.command, exitCode: message.exitCode, cancelled: message.cancelled }) })));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let finalResults;
|
|
106
|
+
return { items, entryIds, findFinalResult(text) {
|
|
107
|
+
// Continuations can share a large journal. Index exact, sanitized matches once,
|
|
108
|
+
// retaining the latest assistant ID just as findLast does.
|
|
109
|
+
if (!finalResults) {
|
|
110
|
+
finalResults = new Map();
|
|
111
|
+
for (const item of items)
|
|
112
|
+
if (item.kind === "assistant") {
|
|
113
|
+
finalResults.set(stripAcceptanceReport(item.text).trim(), item.id);
|
|
114
|
+
for (const part of item.assistant?.content ?? [])
|
|
115
|
+
if (part.type === "text")
|
|
116
|
+
finalResults.set(stripAcceptanceReport(readableText(part.text)).trim(), item.id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return finalResults.get(text);
|
|
120
|
+
} };
|
|
121
|
+
}
|
|
122
|
+
/** Canonical run output is already validated; never decode arbitrary tool JSON to find an answer. */
|
|
123
|
+
export function withFinalResult(history, output, runId, timestamp) {
|
|
124
|
+
const text = readableText(output).trim();
|
|
125
|
+
if (!text)
|
|
126
|
+
return history;
|
|
127
|
+
const matched = history.finalId ?? (history.findFinalResult ? history.findFinalResult(text) : history.items.findLast((item) => item.kind === "assistant" && (stripAcceptanceReport(item.text).trim() === text
|
|
128
|
+
|| item.assistant?.content.some((part) => part.type === "text" && stripAcceptanceReport(readableText(part.text)).trim() === text)))?.id);
|
|
129
|
+
const existing = history.items.some((item) => item.id === matched) ? matched : undefined;
|
|
130
|
+
const id = `result:${runId}`;
|
|
131
|
+
let items;
|
|
132
|
+
return { get items() { return items ??= existing ? history.items.map((item) => item.id === existing ? { ...item, assistant: undefined, text } : item) : [...history.items, { id, kind: "assistant", title: "Saved result", text, timestamp }]; },
|
|
133
|
+
entryIds: existing ? history.entryIds : [...history.entryIds, id], finalId: existing ?? id,
|
|
134
|
+
findFinalResult: existing ? history.findFinalResult : (value) => value === text ? id : history.findFinalResult?.(value),
|
|
135
|
+
deliveredMessages: existing ? history.deliveredMessages : history.deliveredMessages && new Map([...history.deliveredMessages.keys()].map((id) => [id, true])),
|
|
136
|
+
configuration: history.configuration, get unavailable() { return history.unavailable; } };
|
|
137
|
+
}
|
|
138
|
+
/** A page retains only bounded previews; full selected records are validated by the process. */
|
|
139
|
+
export async function indexedHistory(index, input) {
|
|
140
|
+
const page = await index.historyPage(input);
|
|
141
|
+
const entries = new Map(page.entries.map((entry) => [entry.id, entry]));
|
|
142
|
+
const boundary = { terminalEntryId: input.terminalEntryId, endedAt: input.endedAt };
|
|
143
|
+
if (input.terminalEntryId && page.terminalSequence === undefined)
|
|
144
|
+
throw new Error("Saved terminal entry is unavailable; retry history.");
|
|
145
|
+
const inBoundary = (entry) => (page.terminalSequence === undefined || entry.sequence <= page.terminalSequence) && (input.endedAt === undefined || (entry.timestamp ?? Infinity) <= input.endedAt);
|
|
146
|
+
const tools = new Map();
|
|
147
|
+
for (const entry of page.entries) {
|
|
148
|
+
const message = entry.entry.message;
|
|
149
|
+
if (message?.role === "assistant")
|
|
150
|
+
for (const part of message.content ?? []) {
|
|
151
|
+
if (part.type === "toolCall")
|
|
152
|
+
tools.set(part.id, { ...tools.get(part.id), call: entry });
|
|
153
|
+
}
|
|
154
|
+
else if (message?.role === "toolResult")
|
|
155
|
+
tools.set(message.toolCallId, { ...tools.get(message.toolCallId), result: entry });
|
|
156
|
+
}
|
|
157
|
+
// Resolve only tools on this page, not the rest of the conversation. Adjacent-page
|
|
158
|
+
// pairing preserves physical reading order and never becomes completion evidence.
|
|
159
|
+
for (const entry of page.entries) {
|
|
160
|
+
const message = entry.entry.message;
|
|
161
|
+
const pairs = message?.role === "assistant" ? (message.content ?? []).filter((part) => part.type === "toolCall").map((part) => ({ toolCallId: part.id, kind: "result" }))
|
|
162
|
+
: message?.role === "toolResult" ? [{ toolCallId: message.toolCallId, kind: "call" }] : [];
|
|
163
|
+
for (const pair of pairs) {
|
|
164
|
+
const paired = tools.get(pair.toolCallId)?.[pair.kind] ?? await index.entry({ runId: input.runId, index: input.index, ...pair, ...boundary, signal: input.signal });
|
|
165
|
+
if (paired && inBoundary(paired))
|
|
166
|
+
entries.set(paired.id, paired);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const history = historyItems([...entries.values()].sort((a, b) => a.sequence - b.sequence).map((entry) => entry.entry));
|
|
170
|
+
const visible = new Set(page.entries.map((entry) => entry.id));
|
|
171
|
+
history.items = history.items.filter((item) => (item.entryIds ?? [item.id]).some((id) => visible.has(id.split(":")[0])));
|
|
172
|
+
// Only page-local IDs advance the read marker; paired records can live elsewhere.
|
|
173
|
+
history.entryIds = history.entryIds.filter((id) => visible.has(id.split(":")[0]));
|
|
174
|
+
history.unavailable = page.unavailable;
|
|
175
|
+
history.configuration = page.configuration;
|
|
176
|
+
history.deliveredMessages = new Map(page.deliveredMessages);
|
|
177
|
+
history.finalId = page.finalResultId;
|
|
178
|
+
for (const item of history.items)
|
|
179
|
+
item.load = async () => {
|
|
180
|
+
const ids = new Set((item.entryIds ?? [item.id]).map((id) => id.split(":")[0]));
|
|
181
|
+
const full = [];
|
|
182
|
+
for (const id of ids) {
|
|
183
|
+
const entry = entries.get(id);
|
|
184
|
+
if (!entry)
|
|
185
|
+
throw new Error("Selected native entry is unavailable; retry history.");
|
|
186
|
+
const record = await index.record({ runId: input.runId, index: input.index, ref: entry.ref, ...boundary, signal: input.signal });
|
|
187
|
+
if (!record)
|
|
188
|
+
throw new Error("Selected native entry is unavailable; retry history.");
|
|
189
|
+
full.push({ ...record, id });
|
|
190
|
+
}
|
|
191
|
+
const detail = historyItems(full).items.find((candidate) => candidate.id === item.id);
|
|
192
|
+
if (!detail)
|
|
193
|
+
throw new Error("Selected native entry is unavailable; retry history.");
|
|
194
|
+
return detail;
|
|
195
|
+
};
|
|
196
|
+
return { history, page };
|
|
197
|
+
}
|