@manny-est/node-red-flowpilot 0.4.1 → 0.5.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 +27 -0
- package/PROJECT-OVERVIEW.md +7 -7
- package/README.md +14 -8
- package/USER-GUIDE.md +27 -10
- package/flowpilot-core.css +44 -0
- package/flowpilot.js +38 -135
- package/lib/build-core-script.js +44 -0
- package/lib/build-system-prompt.js +6 -0
- package/lib/core/apply-review.js +1677 -0
- package/lib/core/history.js +93 -0
- package/lib/core/init.js +1510 -0
- package/lib/core/main.js +1710 -0
- package/lib/core/markdown.js +165 -0
- package/lib/core/modes.js +1567 -0
- package/lib/core/redaction.js +165 -0
- package/lib/core/selection-context.js +211 -0
- package/lib/envelope.js +136 -0
- package/lib/modify-system-prompt.js +3 -1
- package/lib/storage.js +4 -0
- package/package.json +1 -2
- package/flowpilot-core.js +0 -6427
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------
|
|
2
|
+
// Identifies this conversation for server-side transcript
|
|
3
|
+
// persistence (chats/<conversationId>.jsonl). Kept in sessionStorage so
|
|
4
|
+
// a page reload continues the same transcript; reset by clearChat()
|
|
5
|
+
// ("start a fresh conversation" gets a fresh transcript file too).
|
|
6
|
+
// ---------------------------------------------------------------------
|
|
7
|
+
function makeConversationId() {
|
|
8
|
+
if (window.crypto && typeof window.crypto.randomUUID === "function") {
|
|
9
|
+
return window.crypto.randomUUID();
|
|
10
|
+
}
|
|
11
|
+
return "fp-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function newConversationId() {
|
|
15
|
+
var id = makeConversationId();
|
|
16
|
+
try { sessionStorage.setItem("fp-conversation-id", id); } catch (e) { /* storage unavailable */ }
|
|
17
|
+
return id;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
var conversationId = (function () {
|
|
21
|
+
try {
|
|
22
|
+
var existing = sessionStorage.getItem("fp-conversation-id");
|
|
23
|
+
if (existing) { return existing; }
|
|
24
|
+
} catch (e) { /* storage unavailable */ }
|
|
25
|
+
return newConversationId();
|
|
26
|
+
})();
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------
|
|
29
|
+
// Client-held conversation history. The backend is stateless —
|
|
30
|
+
// each request that should have continuity carries a capped slice of
|
|
31
|
+
// this array. Cleared by clearChat() ("start a fresh conversation").
|
|
32
|
+
// Entries are { role: "user" | "assistant", content: <string> }.
|
|
33
|
+
// ---------------------------------------------------------------------
|
|
34
|
+
var conversationHistory = [];
|
|
35
|
+
|
|
36
|
+
function pushHistory(role, content) {
|
|
37
|
+
if (!content) { return; }
|
|
38
|
+
conversationHistory.push({ role: role, content: String(content) });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Every turn pushes its "user" entry before the request goes out (see
|
|
42
|
+
// send()'s comment), but a stopped/errored/empty turn never gets a
|
|
43
|
+
// matching assistant reply. Left alone, that dangling "user" entry sits
|
|
44
|
+
// at the end of conversationHistory and the NEXT turn's own "user" push
|
|
45
|
+
// lands right after it — two consecutive "user" entries with no
|
|
46
|
+
// assistant turn between them, corrupting the role-alternation shape of
|
|
47
|
+
// every request built from history from then on. Called from every
|
|
48
|
+
// failure exit (chat and generate/document/modify/build alike) to undo
|
|
49
|
+
// exactly that push. Safe even if called when nothing needs undoing: it
|
|
50
|
+
// only pops when the most recent entry is a "user" turn.
|
|
51
|
+
function popDanglingUserHistory() {
|
|
52
|
+
var last = conversationHistory[conversationHistory.length - 1];
|
|
53
|
+
if (last && last.role === "user") {
|
|
54
|
+
conversationHistory.pop();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getHistoryMaxExchanges() {
|
|
59
|
+
var n = Number(currentSettings.historyMaxExchanges);
|
|
60
|
+
return (isFinite(n) && n >= 0) ? n : 10;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Returns the history to send with a request, plus whether anything has
|
|
64
|
+
// ever been dropped. ONE place both /chat and the generate/modify/document
|
|
65
|
+
// send paths call, so the cap and truncation behaviour can't drift
|
|
66
|
+
// between them.
|
|
67
|
+
//
|
|
68
|
+
// B3: stepped (paged) truncation instead of a continuously-sliding
|
|
69
|
+
// window. A plain slice(-maxMessages) would drop the oldest exchange and
|
|
70
|
+
// append the newest on EVERY turn once the cap is reached — changing the
|
|
71
|
+
// history prefix sent to the provider every request and invalidating its
|
|
72
|
+
// prompt/KV cache for the (large, expensive) system prompt every time.
|
|
73
|
+
// Instead, conversationHistory grows untrimmed — and the sent history is
|
|
74
|
+
// a pure append, i.e. byte-stable except for new messages at the tail —
|
|
75
|
+
// up to 2x the cap, then drops the oldest half in one shot. Sent history
|
|
76
|
+
// size ranges between maxMessages and 2*maxMessages exchanges-worth of
|
|
77
|
+
// messages; "truncated" (and HISTORY_TRUNCATION_NOTICE) flips at each of
|
|
78
|
+
// those two points, not every turn.
|
|
79
|
+
function buildHistoryPayload() {
|
|
80
|
+
var maxMessages = getHistoryMaxExchanges() * 2;
|
|
81
|
+
// maxMessages === 0 means memory is off by design — that's not
|
|
82
|
+
// "truncation" and shouldn't trigger the omitted-messages notice (#10).
|
|
83
|
+
if (maxMessages === 0) {
|
|
84
|
+
return { messages: [], truncated: false };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (conversationHistory.length > maxMessages * 2) {
|
|
88
|
+
conversationHistory = conversationHistory.slice(-maxMessages);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var truncated = conversationHistory.length > maxMessages;
|
|
92
|
+
return { messages: conversationHistory.slice(), truncated: truncated };
|
|
93
|
+
}
|