@huseynsnmz/pi-suite 0.0.0-stage → 0.2.0
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 +28 -0
- package/LICENSE +21 -0
- package/README.md +88 -2
- package/SECURITY.md +5 -0
- package/package.json +112 -4
- package/packages/board/CHANGELOG.md +340 -0
- package/packages/board/CONTRIBUTING.md +7 -0
- package/packages/board/LICENSE +21 -0
- package/packages/board/README.md +88 -0
- package/packages/board/SECURITY.md +19 -0
- package/packages/board/docs/how-it-works.md +31 -0
- package/packages/board/docs/reference.md +278 -0
- package/packages/board/package.json +81 -0
- package/packages/board/runner/job-runner-legacy.mjs +68 -0
- package/packages/board/runner/job-runner.mjs +834 -0
- package/packages/board/runner/pty-runner-legacy.mjs +50 -0
- package/packages/board/runner/pty-runner.mjs +2175 -0
- package/packages/board/runner/state-coordinator.mjs +673 -0
- package/packages/board/runner/state-runner.mjs +203 -0
- package/packages/board/runner/title-runner.mjs +80 -0
- package/packages/board/src/commands/agent-board.ts +394 -0
- package/packages/board/src/commands/attach-decision.mjs +66 -0
- package/packages/board/src/commands/attach-flow.ts +251 -0
- package/packages/board/src/commands/bg.ts +103 -0
- package/packages/board/src/commands/board-model.ts +21 -0
- package/packages/board/src/core/atomic.mjs +184 -0
- package/packages/board/src/core/attach-delete.mjs +24 -0
- package/packages/board/src/core/attach-frame-proof.mjs +870 -0
- package/packages/board/src/core/attach-frame-reporter.mjs +648 -0
- package/packages/board/src/core/attach-frame-stream.mjs +266 -0
- package/packages/board/src/core/attach-view-proof.mjs +709 -0
- package/packages/board/src/core/auto-state.mjs +377 -0
- package/packages/board/src/core/background-runs.mjs +273 -0
- package/packages/board/src/core/code-refs-store.mjs +315 -0
- package/packages/board/src/core/code-refs.mjs +913 -0
- package/packages/board/src/core/config.mjs +77 -0
- package/packages/board/src/core/control-protocol.mjs +482 -0
- package/packages/board/src/core/coordinator-client.mjs +633 -0
- package/packages/board/src/core/coordinator-journal.mjs +282 -0
- package/packages/board/src/core/coordinator-protocol.mjs +14 -0
- package/packages/board/src/core/cwd-stats.mjs +114 -0
- package/packages/board/src/core/dashboard-render.mjs +10 -0
- package/packages/board/src/core/derive.mjs +117 -0
- package/packages/board/src/core/diagnostics.mjs +133 -0
- package/packages/board/src/core/double-press.mjs +26 -0
- package/packages/board/src/core/editor-state-reporter.mjs +112 -0
- package/packages/board/src/core/events.mjs +286 -0
- package/packages/board/src/core/evidence.mjs +245 -0
- package/packages/board/src/core/follow-up-queue.mjs +211 -0
- package/packages/board/src/core/foreground-preview-cache.mjs +117 -0
- package/packages/board/src/core/foreground-projection-cache.mjs +99 -0
- package/packages/board/src/core/frame-witness-update.mjs +35 -0
- package/packages/board/src/core/heuristics.mjs +313 -0
- package/packages/board/src/core/hold-input.mjs +27 -0
- package/packages/board/src/core/host-coordination.mjs +300 -0
- package/packages/board/src/core/host-crash.mjs +79 -0
- package/packages/board/src/core/host-probe.mjs +207 -0
- package/packages/board/src/core/host-protocol.mjs +73 -0
- package/packages/board/src/core/ids.mjs +35 -0
- package/packages/board/src/core/ime-cursor-coalesce.mjs +193 -0
- package/packages/board/src/core/invocation.mjs +43 -0
- package/packages/board/src/core/launch-options.mjs +418 -0
- package/packages/board/src/core/launch.mjs +157 -0
- package/packages/board/src/core/load-config.mjs +4 -0
- package/packages/board/src/core/locks.mjs +530 -0
- package/packages/board/src/core/paths.mjs +183 -0
- package/packages/board/src/core/pid.mjs +75 -0
- package/packages/board/src/core/prewarm-schedule.mjs +41 -0
- package/packages/board/src/core/process-identity-probe.mjs +228 -0
- package/packages/board/src/core/prompt-transport.mjs +13 -0
- package/packages/board/src/core/pty-attach-jiggle-controller.mjs +356 -0
- package/packages/board/src/core/pty-attach-jiggle-retry.mjs +109 -0
- package/packages/board/src/core/pty-attach-reconnect.mjs +61 -0
- package/packages/board/src/core/pty-attach-render.mjs +229 -0
- package/packages/board/src/core/pty-links.mjs +81 -0
- package/packages/board/src/core/pty-scroll.mjs +522 -0
- package/packages/board/src/core/pty-support.mjs +327 -0
- package/packages/board/src/core/repo.mjs +103 -0
- package/packages/board/src/core/rows.mjs +414 -0
- package/packages/board/src/core/screen-log-gc.mjs +198 -0
- package/packages/board/src/core/screen-log.mjs +160 -0
- package/packages/board/src/core/session-view.mjs +174 -0
- package/packages/board/src/core/slash-commands.mjs +52 -0
- package/packages/board/src/core/state-commands.mjs +757 -0
- package/packages/board/src/core/status-consistency.mjs +98 -0
- package/packages/board/src/core/steering-prompts.mjs +34 -0
- package/packages/board/src/core/steering.mjs +133 -0
- package/packages/board/src/core/stop-latch.mjs +41 -0
- package/packages/board/src/core/store.mjs +523 -0
- package/packages/board/src/core/suspend.mjs +36 -0
- package/packages/board/src/core/terminal-attach-client.mjs +981 -0
- package/packages/board/src/core/terminal-attach-protocol.mjs +460 -0
- package/packages/board/src/core/terminal-model.mjs +222 -0
- package/packages/board/src/core/terminal-query-sequences.mjs +239 -0
- package/packages/board/src/core/terminal-reply-guard.mjs +222 -0
- package/packages/board/src/core/terminal-snapshot.mjs +440 -0
- package/packages/board/src/core/test-knobs.mjs +28 -0
- package/packages/board/src/core/title.mjs +43 -0
- package/packages/board/src/core/types.mjs +414 -0
- package/packages/board/src/core/warm-host-sweeper.mjs +168 -0
- package/packages/board/src/core/worktree.mjs +65 -0
- package/packages/board/src/index.ts +303 -0
- package/packages/board/src/runtime/service.mjs +3222 -0
- package/packages/board/src/ui/chrome.ts +20 -0
- package/packages/board/src/ui/dashboard-decisions.mjs +55 -0
- package/packages/board/src/ui/dashboard-evidence.mjs +85 -0
- package/packages/board/src/ui/dashboard.ts +2237 -0
- package/packages/board/src/ui/double-left.ts +30 -0
- package/packages/board/src/ui/external-editor.ts +25 -0
- package/packages/board/src/ui/focus.ts +32 -0
- package/packages/board/src/ui/pty-attach.ts +3184 -0
- package/packages/board/src/ui/waiting.ts +30 -0
- package/packages/commands/LICENSE +21 -0
- package/packages/commands/README.md +19 -0
- package/packages/commands/package.json +22 -0
- package/packages/commands/src/index.ts +43 -0
- package/packages/commands/src/security-review.ts +109 -0
- package/packages/frame/CHANGELOG.md +113 -0
- package/packages/frame/CONTRIBUTING.md +7 -0
- package/packages/frame/LICENSE +22 -0
- package/packages/frame/README.md +105 -0
- package/packages/frame/SECURITY.md +16 -0
- package/packages/frame/docs/how-it-works.md +53 -0
- package/packages/frame/docs/reference.md +78 -0
- package/packages/frame/package.json +72 -0
- package/packages/frame/src/data/git.ts +90 -0
- package/packages/frame/src/data/github.ts +140 -0
- package/packages/frame/src/data/gitlab.ts +63 -0
- package/packages/frame/src/data/mode.ts +49 -0
- package/packages/frame/src/data/past-prompts.ts +57 -0
- package/packages/frame/src/data/runner.ts +32 -0
- package/packages/frame/src/data/snapshot.ts +223 -0
- package/packages/frame/src/data/start-notes.ts +104 -0
- package/packages/frame/src/data/telemetry.ts +337 -0
- package/packages/frame/src/data/todos.ts +162 -0
- package/packages/frame/src/data/totals.ts +94 -0
- package/packages/frame/src/data/visits.ts +45 -0
- package/packages/frame/src/index.ts +696 -0
- package/packages/frame/src/kit/LICENSE +21 -0
- package/packages/frame/src/kit/app/attribution.ts +30 -0
- package/packages/frame/src/kit/app/builder.ts +186 -0
- package/packages/frame/src/kit/app/claim.ts +27 -0
- package/packages/frame/src/kit/app/feature.ts +91 -0
- package/packages/frame/src/kit/app/host.ts +214 -0
- package/packages/frame/src/kit/app/renderers.ts +62 -0
- package/packages/frame/src/kit/app/scope.ts +125 -0
- package/packages/frame/src/kit/board/model.ts +337 -0
- package/packages/frame/src/kit/contract.ts +79 -0
- package/packages/frame/src/kit/contracts/accounts.ts +33 -0
- package/packages/frame/src/kit/contracts/ask.ts +120 -0
- package/packages/frame/src/kit/contracts/board.ts +6 -0
- package/packages/frame/src/kit/contracts/by.ts +22 -0
- package/packages/frame/src/kit/contracts/calls.ts +46 -0
- package/packages/frame/src/kit/contracts/events.ts +89 -0
- package/packages/frame/src/kit/contracts/mode.ts +26 -0
- package/packages/frame/src/kit/contracts/names.ts +12 -0
- package/packages/frame/src/kit/contracts/peers.ts +77 -0
- package/packages/frame/src/kit/contracts/prompts.ts +8 -0
- package/packages/frame/src/kit/contracts/screen.ts +75 -0
- package/packages/frame/src/kit/contracts/skills.ts +81 -0
- package/packages/frame/src/kit/contracts/sources.ts +81 -0
- package/packages/frame/src/kit/contracts/views.ts +42 -0
- package/packages/frame/src/kit/control.ts +59 -0
- package/packages/frame/src/kit/dark-theme.ts +41 -0
- package/packages/frame/src/kit/decode.ts +210 -0
- package/packages/frame/src/kit/events/log.ts +313 -0
- package/packages/frame/src/kit/format.ts +25 -0
- package/packages/frame/src/kit/host.ts +4 -0
- package/packages/frame/src/kit/index.ts +25 -0
- package/packages/frame/src/kit/legacy-tasks.ts +39 -0
- package/packages/frame/src/kit/overlay-images.ts +36 -0
- package/packages/frame/src/kit/paths.ts +16 -0
- package/packages/frame/src/kit/screen/client.ts +74 -0
- package/packages/frame/src/kit/screen/feature.ts +64 -0
- package/packages/frame/src/kit/screen/model.ts +182 -0
- package/packages/frame/src/kit/screen/view.ts +838 -0
- package/packages/frame/src/kit/settings/files.ts +90 -0
- package/packages/frame/src/kit/settings/setting.ts +57 -0
- package/packages/frame/src/kit/settings/store.ts +192 -0
- package/packages/frame/src/kit/sources/file.ts +129 -0
- package/packages/frame/src/kit/sources/follow.ts +155 -0
- package/packages/frame/src/kit/testing.ts +260 -0
- package/packages/frame/src/kit/totals.ts +72 -0
- package/packages/frame/src/kit/ui/entry.ts +75 -0
- package/packages/frame/src/kit/ui/frame.ts +212 -0
- package/packages/frame/src/kit/ui/glyphs.ts +132 -0
- package/packages/frame/src/kit/ui/scroll.ts +14 -0
- package/packages/frame/src/render/diff.ts +157 -0
- package/packages/frame/src/render/fit.ts +86 -0
- package/packages/frame/src/render/format.ts +108 -0
- package/packages/frame/src/render/frame.ts +106 -0
- package/packages/frame/src/render/glyphs.ts +189 -0
- package/packages/frame/src/render/keys.ts +43 -0
- package/packages/frame/src/render/lead.ts +37 -0
- package/packages/frame/src/render/paint.ts +135 -0
- package/packages/frame/src/render/safe.ts +19 -0
- package/packages/frame/src/render/segments.ts +735 -0
- package/packages/frame/src/render/streaming.ts +223 -0
- package/packages/frame/src/render/timing.ts +106 -0
- package/packages/frame/src/render/tools.ts +167 -0
- package/packages/frame/src/settings.ts +105 -0
- package/packages/frame/src/ui/editor.ts +272 -0
- package/packages/frame/src/ui/effort.ts +36 -0
- package/packages/frame/src/ui/footer.ts +165 -0
- package/packages/frame/src/ui/header.ts +362 -0
- package/packages/frame/src/ui/history-search.ts +23 -0
- package/packages/frame/src/ui/interrupt.ts +21 -0
- package/packages/frame/src/ui/outputs.ts +170 -0
- package/packages/frame/src/ui/pi-entries.ts +298 -0
- package/packages/frame/src/ui/previews.ts +72 -0
- package/packages/frame/src/ui/queue.ts +75 -0
- package/packages/frame/src/ui/screen.ts +59 -0
- package/packages/frame/src/ui/timings.ts +48 -0
- package/packages/frame/src/ui/todos.ts +205 -0
- package/packages/frame/src/ui/tool-frame.ts +595 -0
- package/packages/frame/src/ui/working.ts +25 -0
- package/packages/frame/src/view/state.ts +97 -0
- package/packages/frame/src/view/transcript.ts +175 -0
- package/packages/frame/src/view/view.ts +162 -0
- package/packages/memory/CONTRIBUTING.md +7 -0
- package/packages/memory/LICENSE +21 -0
- package/packages/memory/README.md +44 -0
- package/packages/memory/SECURITY.md +20 -0
- package/packages/memory/package.json +55 -0
- package/packages/memory/skills/review-memory/SKILL.md +48 -0
- package/packages/memory/src/index.ts +163 -0
- package/packages/memory/src/prompt.ts +25 -0
- package/packages/memory/src/review.ts +150 -0
- package/packages/memory/src/secrets.ts +22 -0
- package/packages/memory/src/store.ts +173 -0
- package/packages/modes/CHANGELOG.md +104 -0
- package/packages/modes/CONTRIBUTING.md +7 -0
- package/packages/modes/LICENSE +21 -0
- package/packages/modes/README.md +174 -0
- package/packages/modes/SECURITY.md +17 -0
- package/packages/modes/package.json +55 -0
- package/packages/modes/skills/review-permissions/SKILL.md +54 -0
- package/packages/modes/src/bash.ts +218 -0
- package/packages/modes/src/deny-off.ts +62 -0
- package/packages/modes/src/git-safety.ts +72 -0
- package/packages/modes/src/index.ts +338 -0
- package/packages/modes/src/interview.ts +206 -0
- package/packages/modes/src/judge-gate.ts +113 -0
- package/packages/modes/src/modes.ts +121 -0
- package/packages/modes/src/review/apply.mjs +156 -0
- package/packages/modes/src/review/collect.mjs +208 -0
- package/packages/modes/src/review/tools.ts +71 -0
- package/packages/modes/src/sandbox.ts +37 -0
- package/packages/router/CHANGELOG.md +8 -0
- package/packages/router/LICENSE +21 -0
- package/packages/router/README.md +90 -0
- package/packages/router/package.json +51 -0
- package/packages/router/src/config.ts +65 -0
- package/packages/router/src/index.ts +172 -0
- package/packages/router/src/judge.ts +51 -0
- package/packages/router/src/route.ts +67 -0
- package/packages/web/CHANGELOG.md +36 -0
- package/packages/web/LICENSE +21 -0
- package/packages/web/README.md +87 -0
- package/packages/web/SECURITY.md +13 -0
- package/packages/web/package.json +59 -0
- package/packages/web/src/config.ts +109 -0
- package/packages/web/src/dnsfilter.ts +97 -0
- package/packages/web/src/extract.ts +65 -0
- package/packages/web/src/index.ts +217 -0
- package/packages/web/src/markdown.ts +122 -0
- package/packages/web/src/net.ts +109 -0
- package/packages/web/src/reader.ts +73 -0
- package/packages/web/src/sanitize.ts +50 -0
- package/packages/web/src/search.ts +76 -0
- package/packages/web/src/secrets.ts +22 -0
- package/packages/web/src/validate.ts +186 -0
- package/src/index.ts +156 -0
- package/src/settings.ts +79 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner-side DCS frame-marker stream parser (issue #222, step 23b1, FW-03).
|
|
3
|
+
*
|
|
4
|
+
* The child reporter writes FROZEN DCS markers into the same ordered stdout stream as
|
|
5
|
+
* the TUI output (`src/core/attach-frame-proof.mjs` owns the literals and validation).
|
|
6
|
+
* This module splits that stream at exact marker boundaries so marker bytes never reach
|
|
7
|
+
* the canonical xterm model, the screen log, the outer Terminal, user input or legacy
|
|
8
|
+
* replay output.
|
|
9
|
+
*
|
|
10
|
+
* ## Contract
|
|
11
|
+
*
|
|
12
|
+
* - Chunks are arbitrary: a marker may be split across PTY reads or coalesced with
|
|
13
|
+
* ordinary output. A bounded pending tail (<= `MAX_PENDING_MARKER_TAIL` code units)
|
|
14
|
+
* carries an incomplete DCS tail into the next chunk.
|
|
15
|
+
* - A complete marker is validated by `decodeFrameMarker` and additionally against the
|
|
16
|
+
* reporter instance: token match, S -> F/A order, strictly increasing frameId,
|
|
17
|
+
* one open candidate at a time, no duplicate F/A, no F/A without S. Any violation is a
|
|
18
|
+
* `protocol-error` — never a proof. Marker bytes are dropped in every case.
|
|
19
|
+
* - Unknown non-marker DCS (`ESC P …`) is ordinary output and keeps legacy behaviour.
|
|
20
|
+
* A DCS that starts like the marker family but never validates is dropped with a
|
|
21
|
+
* bounded diagnostic and can never enable modern proof.
|
|
22
|
+
* - `proof.seq` is assigned by the CALLER to the sequence of the preceding ordinary
|
|
23
|
+
* output SEGMENT (not a child frameId, not socket timing). Multiple proofs at the same
|
|
24
|
+
* seq keep production order.
|
|
25
|
+
* - A pending partial marker at `finish()` is invalid, never a proof.
|
|
26
|
+
*
|
|
27
|
+
* `seq` values here are placeholders (`seq: null`); `runner/pty-runner.mjs` fills them
|
|
28
|
+
* with the real output sequence of the preceding segment.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { decodeFrameMarker } from "./attach-frame-proof.mjs";
|
|
32
|
+
|
|
33
|
+
export const FRAME_STREAM_PROTOCOL = 1;
|
|
34
|
+
export const MAX_PENDING_MARKER_TAIL = 4096;
|
|
35
|
+
export const MAX_STREAM_DIAGNOSTICS = 64;
|
|
36
|
+
export const MAX_PROOF_HISTORY = 256;
|
|
37
|
+
export const MAX_FINISHED_FRAME_IDS = 4096;
|
|
38
|
+
|
|
39
|
+
const DCS_INTRO = "\u001bP";
|
|
40
|
+
const MARKER_PREFIX = "\u001bP> agent-board-frame/1 ; ";
|
|
41
|
+
|
|
42
|
+
function bytes(text) {
|
|
43
|
+
return Buffer.byteLength(text, "utf8");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {{token: string, instanceId: string, sessionEpoch?: number, maxPending?: number}} options
|
|
48
|
+
*/
|
|
49
|
+
export function createFrameStreamParser({ token, instanceId, sessionEpoch = 0, maxPending = MAX_PENDING_MARKER_TAIL }) {
|
|
50
|
+
if (typeof token !== "string" || token.length === 0) throw new Error("frame stream parser requires a reporter token");
|
|
51
|
+
const state = {
|
|
52
|
+
token,
|
|
53
|
+
instanceId,
|
|
54
|
+
sessionEpoch,
|
|
55
|
+
pending: "",
|
|
56
|
+
pendingBytes: 0,
|
|
57
|
+
lastFrameId: 0,
|
|
58
|
+
openFrameId: null,
|
|
59
|
+
finishedFrameIds: new Set(),
|
|
60
|
+
proofs: [],
|
|
61
|
+
diagnostics: [],
|
|
62
|
+
segments: 0,
|
|
63
|
+
markers: 0,
|
|
64
|
+
droppedProofs: 0,
|
|
65
|
+
droppedFinishedIds: 0,
|
|
66
|
+
};
|
|
67
|
+
const diagnostic = (reason, extra = {}) => {
|
|
68
|
+
state.diagnostics.push({ reason, ...extra });
|
|
69
|
+
if (state.diagnostics.length > MAX_STREAM_DIAGNOSTICS) state.diagnostics.splice(0, state.diagnostics.length - MAX_STREAM_DIAGNOSTICS);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Accept one marker literal; returns a proof part or null (with a diagnostic). */
|
|
73
|
+
function acceptMarker(literal) {
|
|
74
|
+
if (bytes(literal) > maxPending) {
|
|
75
|
+
diagnostic("marker-oversize", { bytes: bytes(literal) });
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
let decoded = null;
|
|
79
|
+
try {
|
|
80
|
+
decoded = decodeFrameMarker(literal);
|
|
81
|
+
} catch {
|
|
82
|
+
decoded = null;
|
|
83
|
+
}
|
|
84
|
+
if (decoded === null) {
|
|
85
|
+
diagnostic("marker-invalid");
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (decoded.token !== state.token) {
|
|
89
|
+
diagnostic("marker-wrong-token");
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (decoded.kind === "S") {
|
|
93
|
+
if (state.openFrameId !== null) {
|
|
94
|
+
diagnostic("marker-s-with-open-candidate", { openFrameId: state.openFrameId, frameId: decoded.frameId });
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (state.finishedFrameIds.has(decoded.frameId)) {
|
|
98
|
+
diagnostic("marker-duplicate-s", { frameId: decoded.frameId });
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (decoded.frameId <= state.lastFrameId) {
|
|
102
|
+
diagnostic("marker-frameid-regression", { frameId: decoded.frameId, lastFrameId: state.lastFrameId });
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
state.lastFrameId = decoded.frameId;
|
|
106
|
+
state.openFrameId = decoded.frameId;
|
|
107
|
+
state.markers += 1;
|
|
108
|
+
// Additive S/candidate part: F/A proofs and `state.proofs` semantics stay
|
|
109
|
+
// unchanged; the runner routes this record separately.
|
|
110
|
+
return {
|
|
111
|
+
kind: "candidate",
|
|
112
|
+
candidate: {
|
|
113
|
+
marker: "S",
|
|
114
|
+
frameId: decoded.frameId,
|
|
115
|
+
seq: null,
|
|
116
|
+
sessionEpoch: state.sessionEpoch,
|
|
117
|
+
intentId: decoded.intentId,
|
|
118
|
+
sampledCols: decoded.sampledCols,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (state.openFrameId === null || state.openFrameId !== decoded.frameId) {
|
|
123
|
+
diagnostic("marker-close-without-open-s", { frameId: decoded.frameId, openFrameId: state.openFrameId });
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
state.openFrameId = null;
|
|
127
|
+
state.finishedFrameIds.add(decoded.frameId);
|
|
128
|
+
if (state.finishedFrameIds.size > MAX_FINISHED_FRAME_IDS) {
|
|
129
|
+
const oldest = state.finishedFrameIds.values().next().value;
|
|
130
|
+
state.finishedFrameIds.delete(oldest);
|
|
131
|
+
state.droppedFinishedIds += 1;
|
|
132
|
+
}
|
|
133
|
+
state.markers += 1;
|
|
134
|
+
// F5/F8: the ACCEPTED proof is stored here and the SAME object is returned, so the
|
|
135
|
+
// runner can assign `proof.seq` and snapshot/finish report the real proofs (no
|
|
136
|
+
// second divergent list).
|
|
137
|
+
const proof = {
|
|
138
|
+
marker: decoded.kind,
|
|
139
|
+
frameId: decoded.frameId,
|
|
140
|
+
seq: null,
|
|
141
|
+
sessionEpoch: state.sessionEpoch,
|
|
142
|
+
...(decoded.kind === "F" ? { metadata: decoded.metadata } : { reason: decoded.reason }),
|
|
143
|
+
};
|
|
144
|
+
state.proofs.push(proof);
|
|
145
|
+
if (state.proofs.length > MAX_PROOF_HISTORY) {
|
|
146
|
+
state.proofs.shift();
|
|
147
|
+
state.droppedProofs += 1;
|
|
148
|
+
}
|
|
149
|
+
return { kind: "proof", proof };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Feed one arbitrary chunk. Returns ordered parts:
|
|
154
|
+
* `{kind:"output", data}` for ordinary bytes, `{kind:"candidate", candidate}` for an
|
|
155
|
+
* accepted S boundary, and `{kind:"proof", proof}` for a completed F/A witness.
|
|
156
|
+
* S/F/A `seq` is filled by the caller.
|
|
157
|
+
*/
|
|
158
|
+
function push(chunk) {
|
|
159
|
+
const parts = [];
|
|
160
|
+
if (typeof chunk !== "string" || chunk.length === 0) return parts;
|
|
161
|
+
let text = state.pending + chunk;
|
|
162
|
+
state.pending = "";
|
|
163
|
+
state.pendingBytes = 0;
|
|
164
|
+
let index = 0;
|
|
165
|
+
const emitOutput = (data) => {
|
|
166
|
+
if (data.length === 0) return;
|
|
167
|
+
state.segments += 1;
|
|
168
|
+
parts.push({ kind: "output", data });
|
|
169
|
+
};
|
|
170
|
+
while (index < text.length) {
|
|
171
|
+
const intro = text.indexOf(DCS_INTRO, index);
|
|
172
|
+
if (intro < 0) {
|
|
173
|
+
// A trailing lone ESC may be the first byte of a split introducer: carry it.
|
|
174
|
+
if (text.endsWith("\u001b")) {
|
|
175
|
+
emitOutput(text.slice(index, text.length - 1));
|
|
176
|
+
state.pending = "\u001b";
|
|
177
|
+
state.pendingBytes = 1;
|
|
178
|
+
} else {
|
|
179
|
+
emitOutput(text.slice(index));
|
|
180
|
+
}
|
|
181
|
+
index = text.length;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
if (intro > index) emitOutput(text.slice(index, intro));
|
|
185
|
+
const terminator = text.indexOf("\u001b\\", intro + 2);
|
|
186
|
+
const prefixLookalike = text.startsWith(MARKER_PREFIX, intro);
|
|
187
|
+
if (terminator < 0) {
|
|
188
|
+
// Incomplete DCS tail: ALWAYS carry it into the next chunk (bounded), so a
|
|
189
|
+
// marker split character-by-character never leaks bytes. Only a tail that
|
|
190
|
+
// exceeds the bound is resolved here: a marker-family tail is dropped with a
|
|
191
|
+
// diagnostic, any other DCS keeps legacy behaviour and is emitted as output.
|
|
192
|
+
const tail = text.slice(intro);
|
|
193
|
+
if (bytes(tail) > maxPending) {
|
|
194
|
+
if (prefixLookalike || tail.startsWith(MARKER_PREFIX.slice(0, Math.min(tail.length, MARKER_PREFIX.length)))) {
|
|
195
|
+
diagnostic("marker-tail-oversize", { bytes: bytes(tail) });
|
|
196
|
+
} else {
|
|
197
|
+
emitOutput(tail);
|
|
198
|
+
}
|
|
199
|
+
index = text.length;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
state.pending = tail;
|
|
203
|
+
state.pendingBytes = bytes(tail);
|
|
204
|
+
index = text.length;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
const literal = text.slice(intro, terminator + 2);
|
|
208
|
+
if (prefixLookalike) {
|
|
209
|
+
const accepted = acceptMarker(literal);
|
|
210
|
+
if (accepted !== null) parts.push(accepted);
|
|
211
|
+
} else {
|
|
212
|
+
// Unknown DCS is ordinary output (legacy behaviour).
|
|
213
|
+
emitOutput(literal);
|
|
214
|
+
}
|
|
215
|
+
index = terminator + 2;
|
|
216
|
+
}
|
|
217
|
+
text = "";
|
|
218
|
+
return parts;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* End of stream. A pending marker-family tail or an open candidate is invalid (never a
|
|
223
|
+
* proof); a trailing ORDINARY byte (e.g. a lone ESC or a non-marker partial DCS) is
|
|
224
|
+
* returned as `trailing` so the caller can forward it to the normal output path
|
|
225
|
+
* instead of dropping legitimate child output.
|
|
226
|
+
*/
|
|
227
|
+
function finish(reason = "stream-end") {
|
|
228
|
+
let trailing = "";
|
|
229
|
+
if (state.pending.length > 0) {
|
|
230
|
+
// Only an actual marker-family prefix is invalid at end; a lone ESC or any
|
|
231
|
+
// other partial DCS is ordinary child output and must be forwarded.
|
|
232
|
+
const markerFamily = state.pending.startsWith(MARKER_PREFIX) || (state.pending.startsWith("\u001bP") && MARKER_PREFIX.startsWith(state.pending));
|
|
233
|
+
if (markerFamily) {
|
|
234
|
+
diagnostic("pending-marker-at-end", { at: reason, bytes: state.pendingBytes });
|
|
235
|
+
} else {
|
|
236
|
+
trailing = state.pending;
|
|
237
|
+
}
|
|
238
|
+
state.pending = "";
|
|
239
|
+
state.pendingBytes = 0;
|
|
240
|
+
}
|
|
241
|
+
if (state.openFrameId !== null) {
|
|
242
|
+
diagnostic("open-candidate-at-end", { frameId: state.openFrameId, at: reason });
|
|
243
|
+
state.openFrameId = null;
|
|
244
|
+
}
|
|
245
|
+
return { diagnostics: state.diagnostics.slice(), proofs: state.proofs.slice(), trailing };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function snapshot() {
|
|
249
|
+
return {
|
|
250
|
+
protocol: FRAME_STREAM_PROTOCOL,
|
|
251
|
+
instanceId: state.instanceId,
|
|
252
|
+
sessionEpoch: state.sessionEpoch,
|
|
253
|
+
segments: state.segments,
|
|
254
|
+
markers: state.markers,
|
|
255
|
+
lastFrameId: state.lastFrameId,
|
|
256
|
+
openFrameId: state.openFrameId,
|
|
257
|
+
pendingBytes: state.pendingBytes,
|
|
258
|
+
proofs: state.proofs.slice(),
|
|
259
|
+
diagnostics: state.diagnostics.slice(),
|
|
260
|
+
droppedProofs: state.droppedProofs,
|
|
261
|
+
droppedFinishedIds: state.droppedFinishedIds,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return { push, finish, snapshot, state };
|
|
266
|
+
}
|