@manny-est/node-red-flowpilot 0.5.2 → 0.6.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 +85 -26
- package/README.md +10 -1
- package/USER-GUIDE.md +5 -1
- package/flowpilot-core.css +53 -10
- package/flowpilot-node-entry.js +15 -0
- package/flowpilot.js +1207 -187
- package/lib/agent-contract.js +50 -0
- package/lib/build-core-script.js +1 -0
- package/lib/chat-data.js +106 -0
- package/lib/core/apply-review.js +34 -11
- package/lib/core/graph-truth.js +63 -0
- package/lib/core/history.js +157 -6
- package/lib/core/init.js +223 -21
- package/lib/core/main.js +780 -38
- package/lib/core/modes.js +1098 -112
- package/lib/core/selection-context.js +44 -24
- package/lib/default-system-prompt.js +4 -3
- package/lib/document-system-prompt.js +7 -4
- package/lib/envelope.js +13 -7
- package/lib/generation-system-prompt.js +5 -4
- package/lib/modify-system-prompt.js +64 -12
- package/lib/persona-prompt.js +81 -54
- package/lib/prompt-fragments.js +17 -2
- package/lib/provider-anthropic.js +23 -10
- package/lib/provider-openai-compatible.js +51 -11
- package/lib/provider-shape-check.js +34 -0
- package/lib/storage.js +134 -21
- package/package.json +3 -2
package/lib/core/init.js
CHANGED
|
@@ -14,17 +14,20 @@
|
|
|
14
14
|
"- `/generate` — arm Generate mode\n" +
|
|
15
15
|
"- `/document` — arm Document mode\n" +
|
|
16
16
|
"- `/modify` — arm Modify mode\n" +
|
|
17
|
-
"- `/query` — back to Query (disarm)\n" +
|
|
17
|
+
"- `/query` / `/chat` — back to Query (disarm)\n" +
|
|
18
18
|
"- `/clear` — start a fresh conversation (clears chat and memory)\n" +
|
|
19
19
|
"- `/history` — open the Flight log (past conversations)\n" +
|
|
20
20
|
"- `/settings` — open the Hangar (providers, behavior, safety)\n\n" +
|
|
21
21
|
"Typing a shortcut with extra text, e.g. `/modify add a debug node`, switches mode and leaves the rest in the box so you can review before sending.\n\n" +
|
|
22
22
|
"- `/demo` — load a sample Generate request (a dad joke flow) into the compose box\n" +
|
|
23
23
|
"- `/feedback` — bug report / feature request info\n" +
|
|
24
|
+
"- `/beta` — beta-channel install/revert instructions\n" +
|
|
24
25
|
"- `/build` — describe a goal; I'll plan, propose, and walk an iterative build → deploy → debug → review → fix loop with you\n" +
|
|
25
26
|
"- `/compact` — hide labels on the selected node(s) (icon-only); `/expand` restores them. Instant, no AI involved — one Ctrl+Z undoes it.\n" +
|
|
26
27
|
"- `/disable` — disable the selected node(s) (skipped on Deploy); `/enable` re-enables them. Instant, no AI involved — one Ctrl+Z undoes it.\n" +
|
|
27
|
-
"- `/refresh` — re-render all messages from the in-memory record store (restores interactive Apply buttons if they were lost).\n
|
|
28
|
+
"- `/refresh` — re-render all messages from the in-memory record store (restores interactive Apply buttons if they were lost).\n" +
|
|
29
|
+
"- `/debug` — toggle debug logging (full prompts/replies/decisions to `flowpilot/debug.log`) on/off. Instant, no AI involved.\n\n" +
|
|
30
|
+
"- `/summarize` — summarize older chat history into one compact note, keeping the latest two exchanges intact.\n\n" +
|
|
28
31
|
"### Also worth knowing\n\n" +
|
|
29
32
|
"- Action chips (paper-plane buttons) offer a one-click follow-up — review and send, nothing fires automatically.\n" +
|
|
30
33
|
"- When I ask a clarifying question, I'll often offer quick-reply buttons (plus \"Other\" for your own answer) — clicking one sends it right away.\n" +
|
|
@@ -42,11 +45,30 @@
|
|
|
42
45
|
"human crew reads every report.\n\n" +
|
|
43
46
|
"- **Report an issue**: https://github.com/manny-est/flowpilot/issues\n" +
|
|
44
47
|
"- **Browse the repo**: https://github.com/manny-est/flowpilot\n\n" +
|
|
45
|
-
"A good report travels light but packs the essentials: your
|
|
46
|
-
"version
|
|
47
|
-
"reproduce. That's usually enough
|
|
48
|
+
"A good report travels light but packs the essentials: your FlowPilot " +
|
|
49
|
+
"version (top of Settings), your Node-RED version, the provider/model " +
|
|
50
|
+
"you're flying with, and the steps to reproduce. That's usually enough " +
|
|
51
|
+
"to get a fix off the ground.\n\n" +
|
|
48
52
|
"Safe travels.";
|
|
49
53
|
|
|
54
|
+
var BETA_TEXT = "## Beta channel\n\n" +
|
|
55
|
+
"Thanks for testing FlowPilot on the beta track.\n\n" +
|
|
56
|
+
"**Install the beta:**\n" +
|
|
57
|
+
"```\n" +
|
|
58
|
+
"cd ~/.node-red (or your mounted Node-RED data dir, e.g. /data in Docker)\n" +
|
|
59
|
+
"npm install @manny-est/node-red-flowpilot@beta\n" +
|
|
60
|
+
"```\n" +
|
|
61
|
+
"Then **restart Node-RED** (or the container) — a browser refresh alone " +
|
|
62
|
+
"won't pick it up; FlowPilot's editor UI is cached server-side.\n\n" +
|
|
63
|
+
"**Revert to stable:**\n" +
|
|
64
|
+
"```\n" +
|
|
65
|
+
"cd ~/.node-red (or /data)\n" +
|
|
66
|
+
"npm install @manny-est/node-red-flowpilot@latest\n" +
|
|
67
|
+
"```\n" +
|
|
68
|
+
"Restart again afterward.\n\n" +
|
|
69
|
+
"**Found a bug?** Type `/feedback`, or go straight to " +
|
|
70
|
+
"https://github.com/manny-est/flowpilot/issues — every report helps.";
|
|
71
|
+
|
|
50
72
|
var demoTypeTimer = null;
|
|
51
73
|
|
|
52
74
|
// Registry of all slash commands — drives the autocomplete panel that
|
|
@@ -59,14 +81,20 @@
|
|
|
59
81
|
{ cmd: "/build", desc: "Start a deploy-verify build loop" },
|
|
60
82
|
{ cmd: "/chat", desc: "Switch to Chat mode" },
|
|
61
83
|
{ cmd: "/query", desc: "Add or toggle a Query intent" },
|
|
84
|
+
{ cmd: "/clear", desc: "Start a fresh conversation" },
|
|
85
|
+
{ cmd: "/history", desc: "Open past conversations" },
|
|
86
|
+
{ cmd: "/settings", desc: "Open provider/behavior settings" },
|
|
62
87
|
{ cmd: "/compact", desc: "Compact labels on selected nodes" },
|
|
63
88
|
{ cmd: "/expand", desc: "Expand labels on selected nodes" },
|
|
64
89
|
{ cmd: "/disable", desc: "Disable selected nodes" },
|
|
65
90
|
{ cmd: "/enable", desc: "Enable selected nodes" },
|
|
66
91
|
{ cmd: "/refresh", desc: "Re-render all messages from shadow record store" },
|
|
92
|
+
{ cmd: "/debug", desc: "Toggle debug logging on/off" },
|
|
93
|
+
{ cmd: "/summarize", desc: "Summarize older history into a compact note" },
|
|
67
94
|
{ cmd: "/demo", desc: "Type in a demo prompt" },
|
|
68
95
|
{ cmd: "/help", desc: "Show all available commands" },
|
|
69
|
-
{ cmd: "/feedback", desc: "Show feedback info" }
|
|
96
|
+
{ cmd: "/feedback", desc: "Show feedback info" },
|
|
97
|
+
{ cmd: "/beta", desc: "Beta install/revert instructions" }
|
|
70
98
|
];
|
|
71
99
|
|
|
72
100
|
function bindSlashAutocomplete($promptBox) {
|
|
@@ -233,10 +261,29 @@
|
|
|
233
261
|
addMessage("assistant", FEEDBACK_TEXT);
|
|
234
262
|
if ($promptBox.length) { $promptBox.val(""); }
|
|
235
263
|
break;
|
|
264
|
+
case "/beta":
|
|
265
|
+
addMessage("assistant", BETA_TEXT);
|
|
266
|
+
if ($promptBox.length) { $promptBox.val(""); }
|
|
267
|
+
break;
|
|
236
268
|
case "/refresh":
|
|
237
269
|
refreshView();
|
|
238
270
|
if ($promptBox.length) { $promptBox.val(""); }
|
|
239
271
|
break;
|
|
272
|
+
// Deterministic, no AI round-trip: flips the same
|
|
273
|
+
// settings.debugLogging field the Hangar checkbox binds to, via
|
|
274
|
+
// the normal saveSettings() round trip, so the two never drift.
|
|
275
|
+
case "/debug":
|
|
276
|
+
var newDebugState = !el("#fp-debug-logging").prop("checked");
|
|
277
|
+
el("#fp-debug-logging").prop("checked", newDebugState);
|
|
278
|
+
saveSettings(function () {
|
|
279
|
+
addMessage("assistant", "Debug logging is now " + (newDebugState ? "ON" : "OFF") + ".");
|
|
280
|
+
});
|
|
281
|
+
if ($promptBox.length) { $promptBox.val(""); }
|
|
282
|
+
break;
|
|
283
|
+
case "/summarize":
|
|
284
|
+
summarizeConversationHistory();
|
|
285
|
+
if ($promptBox.length) { $promptBox.val(""); }
|
|
286
|
+
break;
|
|
240
287
|
// Deterministic, no LLM round-trip: just invokes Node-RED's own
|
|
241
288
|
// native "show/hide selected node labels" action (RED.actions
|
|
242
289
|
// "core:show-selected-node-labels" / "core:hide-selected-node-
|
|
@@ -567,7 +614,7 @@
|
|
|
567
614
|
$panel.data("fp-review-apply-bound", true);
|
|
568
615
|
var recordId = parseInt($panel.attr("data-fp-record-id"), 10);
|
|
569
616
|
if (isNaN(recordId)) { return; }
|
|
570
|
-
$panel.find(".fp-review-actions button.
|
|
617
|
+
$panel.find(".fp-review-actions button.fp-chip.fp-chip-card:not(:disabled)").on("click", function () {
|
|
571
618
|
var $btn = $(this);
|
|
572
619
|
if ($btn.prop("disabled")) { return; }
|
|
573
620
|
$btn.prop("disabled", true).text("Applying…");
|
|
@@ -579,6 +626,50 @@
|
|
|
579
626
|
});
|
|
580
627
|
}
|
|
581
628
|
|
|
629
|
+
// Interactive question/consent buttons are cloned into the pop-out as
|
|
630
|
+
// HTML, so their main-window click closures do not survive. Relay the
|
|
631
|
+
// record id plus the selected action back to the parent, where the live
|
|
632
|
+
// record still owns the continuation callback/state.
|
|
633
|
+
function bindRecordActionButtons($scope) {
|
|
634
|
+
$scope.filter("[data-fp-record-id]").add($scope.find("[data-fp-record-id]"))
|
|
635
|
+
.filter("button").each(function () {
|
|
636
|
+
var $btn = $(this);
|
|
637
|
+
if ($btn.data("fp-record-action-bound") || $btn.prop("disabled")) { return; }
|
|
638
|
+
var recordId = parseInt($btn.attr("data-fp-record-id"), 10);
|
|
639
|
+
var action = $btn.attr("data-fp-record-action");
|
|
640
|
+
if (isNaN(recordId) || !action) { return; }
|
|
641
|
+
$btn.data("fp-record-action-bound", true).on("click", function () {
|
|
642
|
+
if (action === "show-other") {
|
|
643
|
+
$btn.closest(".fp-question-row").next(".fp-question-other-row")
|
|
644
|
+
.removeClass("fp-hidden").find("input").focus();
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
var relayAction = action;
|
|
648
|
+
var value = $btn.attr("data-fp-record-value") || "";
|
|
649
|
+
if (action === "answer-other") {
|
|
650
|
+
value = $btn.siblings("input").val().trim();
|
|
651
|
+
if (!value) { return; }
|
|
652
|
+
relayAction = "answer";
|
|
653
|
+
}
|
|
654
|
+
var $questionRow = $btn.closest(".fp-question-row");
|
|
655
|
+
var $otherRow = $btn.closest(".fp-question-other-row");
|
|
656
|
+
$questionRow.find("button, input").prop("disabled", true);
|
|
657
|
+
$questionRow.next(".fp-question-other-row").find("button, input").prop("disabled", true);
|
|
658
|
+
$otherRow.find("button, input").prop("disabled", true);
|
|
659
|
+
$otherRow.prev(".fp-question-row").find("button, input").prop("disabled", true);
|
|
660
|
+
if (!window.opener || window.opener.closed) { return; }
|
|
661
|
+
try {
|
|
662
|
+
window.opener.postMessage({
|
|
663
|
+
event: "resolveRecordAction",
|
|
664
|
+
recordId: recordId,
|
|
665
|
+
action: relayAction,
|
|
666
|
+
value: value
|
|
667
|
+
}, location.origin);
|
|
668
|
+
} catch (e) { /* ignore */ }
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
582
673
|
// The loop stepper's "Stop build loop" button is relayed the same
|
|
583
674
|
// generic way as any other chat message (renderLoopStepper appends/
|
|
584
675
|
// replaces a #fp-loop-stepper element, which the MutationObserver
|
|
@@ -743,6 +834,7 @@
|
|
|
743
834
|
' <div class="fp-status-strip">' +
|
|
744
835
|
' <span id="fp-selection-status" class="fp-selection-status">No nodes selected</span>' +
|
|
745
836
|
' <a href="#" id="fp-preview-nodes" class="fp-preview-link fp-hidden" title="Open this from the main window to see the exact sanitized node JSON">Preview JSON</a>' +
|
|
837
|
+
' <span id="fp-dev-warning-status" class="fp-secrets-status" title="Development/test only. Anything you send may leave this Node-RED instance. Don\'t include credentials or proprietary data; local/private AI recommended.">⚠</span>' +
|
|
746
838
|
' <span id="fp-size-status" class="fp-size-status fp-hidden"></span>' +
|
|
747
839
|
' <span id="fp-secrets-status" class="fp-secrets-status fp-hidden" title="Context may include node config and code. Don\'t send credentials or proprietary data. Local/private AI recommended.">⚠</span>' +
|
|
748
840
|
' <span id="fp-debug-status" class="fp-debug-status fp-hidden"></span>' +
|
|
@@ -830,6 +922,7 @@
|
|
|
830
922
|
if (data.event === "initialSync") {
|
|
831
923
|
el("#fp-messages").html(data.html);
|
|
832
924
|
bindReviewApplyButtons(el("#fp-messages"));
|
|
925
|
+
bindRecordActionButtons(el("#fp-messages"));
|
|
833
926
|
bindStopLoopButton(el("#fp-messages"));
|
|
834
927
|
bindTabSwitching(el("#fp-messages"));
|
|
835
928
|
bindDebugAttachButtons(el("#fp-messages"));
|
|
@@ -837,6 +930,7 @@
|
|
|
837
930
|
} else if (data.event === "appendMessage") {
|
|
838
931
|
el("#fp-messages").append(data.html);
|
|
839
932
|
bindReviewApplyButtons(el("#fp-messages").children().last());
|
|
933
|
+
bindRecordActionButtons(el("#fp-messages").children().last());
|
|
840
934
|
bindStopLoopButton(el("#fp-messages").children().last());
|
|
841
935
|
bindTabSwitching(el("#fp-messages").children().last());
|
|
842
936
|
bindDebugAttachButtons(el("#fp-messages").children().last());
|
|
@@ -902,7 +996,7 @@
|
|
|
902
996
|
} else if (data.event === "conversationList") {
|
|
903
997
|
renderHistoryList(data.conversations || []);
|
|
904
998
|
} else if (data.event === "settingsLoaded") {
|
|
905
|
-
fillSettings(data.settings);
|
|
999
|
+
fillSettings(data.settings, "popout settingsLoaded relay");
|
|
906
1000
|
renderIntents(el("#fp-intents"));
|
|
907
1001
|
updateSelectionStatus();
|
|
908
1002
|
}
|
|
@@ -945,11 +1039,7 @@
|
|
|
945
1039
|
' </div>' +
|
|
946
1040
|
|
|
947
1041
|
' <div id="fp-chat-panel" class="fp-panel">' +
|
|
948
|
-
' <div id="fp-
|
|
949
|
-
' <strong>Development/test only.</strong> ' +
|
|
950
|
-
' Anything you send may leave this Node-RED instance. ' +
|
|
951
|
-
' Don\'t include credentials or proprietary data; local/private AI recommended.' +
|
|
952
|
-
' </div>' +
|
|
1042
|
+
' <div id="fp-update-banner" class="fp-warning fp-hidden"></div>' +
|
|
953
1043
|
' <div id="fp-messages" class="fp-messages"></div>' +
|
|
954
1044
|
' <div class="fp-compose">' +
|
|
955
1045
|
' <div class="fp-action-bar">' +
|
|
@@ -972,6 +1062,7 @@
|
|
|
972
1062
|
' <div class="fp-status-strip">' +
|
|
973
1063
|
' <span id="fp-selection-status" class="fp-selection-status">No nodes selected</span>' +
|
|
974
1064
|
' <a href="#" id="fp-preview-nodes" class="fp-preview-link fp-hidden" title="Show the exact sanitized node JSON that will be sent">Preview JSON</a>' +
|
|
1065
|
+
' <span id="fp-dev-warning-status" class="fp-secrets-status" title="Development/test only. Anything you send may leave this Node-RED instance. Don\'t include credentials or proprietary data; local/private AI recommended.">⚠</span>' +
|
|
975
1066
|
' <span id="fp-size-status" class="fp-size-status fp-hidden"></span>' +
|
|
976
1067
|
' <span id="fp-secrets-status" class="fp-secrets-status fp-hidden" title="Context may include node config and code. Don\'t send credentials or proprietary data. Local/private AI recommended.">⚠</span>' +
|
|
977
1068
|
' <span id="fp-debug-status" class="fp-debug-status fp-hidden"></span>' +
|
|
@@ -985,6 +1076,7 @@
|
|
|
985
1076
|
|
|
986
1077
|
' <div id="fp-settings-panel" class="fp-panel fp-hidden">' +
|
|
987
1078
|
' <div class="fp-form">' +
|
|
1079
|
+
' <div id="fp-flowpilot-version" class="fp-consent-hint"></div>' +
|
|
988
1080
|
|
|
989
1081
|
' <details class="fp-settings-group" open>' +
|
|
990
1082
|
' <summary title="Hangar — where your AI providers are configured">Providers</summary>' +
|
|
@@ -1014,6 +1106,11 @@
|
|
|
1014
1106
|
' <input id="fp-model" type="text" list="fp-model-options" placeholder="Model name for this provider">' +
|
|
1015
1107
|
' <datalist id="fp-model-options"></datalist>' +
|
|
1016
1108
|
' <div id="fp-models-hint" class="fp-consent-hint fp-hidden"></div>' +
|
|
1109
|
+
' <label>Context window (tokens, optional)</label>' +
|
|
1110
|
+
' <input id="fp-num-ctx" type="number" min="0" step="1" placeholder="0 = unknown">' +
|
|
1111
|
+
' <div class="fp-consent-hint">If this provider/model has a known context window ' +
|
|
1112
|
+
' (e.g. 128000, 250000 — check the provider’s own docs), set it here so FlowPilot ' +
|
|
1113
|
+
' can warn before a request would overflow it. Leave at 0 if unknown.</div>' +
|
|
1017
1114
|
' <label>Temperature</label>' +
|
|
1018
1115
|
' <input id="fp-temperature" type="number" min="0" max="2" step="0.1" placeholder="0.2">' +
|
|
1019
1116
|
' <div class="fp-consent-hint">Controls randomness. Lower (e.g. 0.2) is more ' +
|
|
@@ -1034,8 +1131,8 @@
|
|
|
1034
1131
|
' </div>' +
|
|
1035
1132
|
|
|
1036
1133
|
' <div class="fp-settings-section">Personality</div>' +
|
|
1037
|
-
' <label>Persona intensity: <span id="fp-persona-value">
|
|
1038
|
-
' <input id="fp-persona-intensity" type="range" min="1" max="
|
|
1134
|
+
' <label>Persona intensity: <span id="fp-persona-value">2</span>/5</label>' +
|
|
1135
|
+
' <input id="fp-persona-intensity" type="range" min="1" max="5" step="1">' +
|
|
1039
1136
|
' <div id="fp-persona-label" class="fp-consent-hint"></div>' +
|
|
1040
1137
|
' <div class="fp-consent-hint">Chat only. Scales the AI\'s voice at greetings, ' +
|
|
1041
1138
|
' capability questions, and brief transitions — 1 is a plain Node-RED engineer, ' +
|
|
@@ -1062,10 +1159,30 @@
|
|
|
1062
1159
|
|
|
1063
1160
|
' <div class="fp-settings-section">Request timeout</div>' +
|
|
1064
1161
|
' <label>Give up after (seconds)</label>' +
|
|
1065
|
-
' <input id="fp-request-timeout" type="number"
|
|
1162
|
+
' <input id="fp-request-timeout" type="number" step="any" placeholder="180">' +
|
|
1066
1163
|
' <div class="fp-consent-hint">How long to wait for a provider response before ' +
|
|
1067
1164
|
' giving up. Raise this if you\'re running a large local model on slow hardware ' +
|
|
1068
1165
|
' (e.g. Ollama without a GPU) and seeing timeout errors.</div>' +
|
|
1166
|
+
' <div class="fp-settings-section">Updates</div>' +
|
|
1167
|
+
' <label class="fp-checkbox-row">' +
|
|
1168
|
+
' <input id="fp-check-for-updates" type="checkbox"> ' +
|
|
1169
|
+
' Check for FlowPilot updates' +
|
|
1170
|
+
' </label>' +
|
|
1171
|
+
' <div class="fp-consent-hint">On by default. Periodically checks npm for a newer ' +
|
|
1172
|
+
' FlowPilot version on your current track (beta or stable) and shows a small ' +
|
|
1173
|
+
' banner if one is available. Uncheck to disable this entirely; no network ' +
|
|
1174
|
+
' request is made when off.</div>' +
|
|
1175
|
+
' <label>Max tokens per model response (agent step)</label>' +
|
|
1176
|
+
' <input id="fp-agent-turn-max-tokens" type="number" min="1" max="65536" step="1" placeholder="4096">' +
|
|
1177
|
+
' <div class="fp-consent-hint">Hard output limit for EACH individual tool-calling ' +
|
|
1178
|
+
' step’s model response. Classic (no-tools) requests are not capped.</div>' +
|
|
1179
|
+
' <label>Max total tokens per agent turn</label>' +
|
|
1180
|
+
' <input id="fp-agent-loop-token-ceiling" type="number" min="1" step="1" placeholder="50000">' +
|
|
1181
|
+
' <div class="fp-consent-hint">Cumulative token budget (prompt + response, summed ' +
|
|
1182
|
+
' across every step) for one multi-step agent turn before FlowPilot stops with an ' +
|
|
1183
|
+
' error instead of continuing. This is the number that appears in the "FlowPilot ' +
|
|
1184
|
+
' stopped after using N tokens" error — raise it here if you’re hitting that message ' +
|
|
1185
|
+
' on a model with a large context window.</div>' +
|
|
1069
1186
|
|
|
1070
1187
|
' <div class="fp-settings-section">Agentic build loop</div>' +
|
|
1071
1188
|
' <label>Max build/fix attempts</label>' +
|
|
@@ -1105,12 +1222,10 @@
|
|
|
1105
1222
|
' <div class="fp-settings-section">Risk warnings</div>' +
|
|
1106
1223
|
' <label class="fp-checkbox-row">' +
|
|
1107
1224
|
' <input id="fp-suppress-warnings" type="checkbox"> ' +
|
|
1108
|
-
' Hide the recurring credentials/size warning
|
|
1225
|
+
' Hide the recurring credentials/size warning' +
|
|
1109
1226
|
' </label>' +
|
|
1110
|
-
' <div class="fp-consent-hint">
|
|
1111
|
-
'
|
|
1112
|
-
' Anything you send may leave this Node-RED instance.</div>' +
|
|
1113
|
-
' <input id="fp-suppress-confirm" type="text" placeholder="Type: I understand the risk">' +
|
|
1227
|
+
' <div class="fp-consent-hint">Anything you send may leave this Node-RED ' +
|
|
1228
|
+
' instance. Check this box to hide the warning indicator near Send.</div>' +
|
|
1114
1229
|
|
|
1115
1230
|
' <div class="fp-settings-section">Redaction</div>' +
|
|
1116
1231
|
' <label class="fp-checkbox-row">' +
|
|
@@ -1125,6 +1240,15 @@
|
|
|
1125
1240
|
' sent either way. To disable, check the box and type ' +
|
|
1126
1241
|
' <strong>disable redaction</strong> below.</div>' +
|
|
1127
1242
|
' <input id="fp-redaction-confirm" type="text" placeholder="Type: disable redaction">' +
|
|
1243
|
+
|
|
1244
|
+
' <div class="fp-settings-section">Debug mode</div>' +
|
|
1245
|
+
' <label class="fp-checkbox-row">' +
|
|
1246
|
+
' <input id="fp-debug-logging" type="checkbox"> ' +
|
|
1247
|
+
' Debug mode' +
|
|
1248
|
+
' </label>' +
|
|
1249
|
+
' <div class="fp-consent-hint">Logs full prompts, replies, and decisions to ' +
|
|
1250
|
+
' <code>flowpilot/debug.log</code> for troubleshooting. Off by default — ' +
|
|
1251
|
+
' this file can get large.</div>' +
|
|
1128
1252
|
' </details>' +
|
|
1129
1253
|
|
|
1130
1254
|
' <div class="fp-settings-actions">' +
|
|
@@ -1287,6 +1411,13 @@
|
|
|
1287
1411
|
RED.events.on("deploy", function () {
|
|
1288
1412
|
if (activeBuildLoop && activeBuildLoop.waypoint === "apply") {
|
|
1289
1413
|
activeBuildLoop.waypoint = "attach";
|
|
1414
|
+
// CLAUDE-025: marks the start of THIS attempt's own
|
|
1415
|
+
// evidence window — see freshBuildLoopEvidence. Anything
|
|
1416
|
+
// that arrived before this (a prior attempt's debug
|
|
1417
|
+
// output, a manual attach, an unrelated still-running
|
|
1418
|
+
// flow) is stale and must not count toward this
|
|
1419
|
+
// attempt's own goal.
|
|
1420
|
+
activeBuildLoop.deployedAt = Date.now();
|
|
1290
1421
|
renderLoopStepper(activeBuildLoop);
|
|
1291
1422
|
// Start a timer so flows with no debug nodes (e.g. HTTP
|
|
1292
1423
|
// endpoints) don't leave the loop stuck silently waiting.
|
|
@@ -1368,6 +1499,36 @@
|
|
|
1368
1499
|
}
|
|
1369
1500
|
rec.state = "applied";
|
|
1370
1501
|
}
|
|
1502
|
+
} else if (data.event === "resolveRecordAction" && typeof data.recordId === "number") {
|
|
1503
|
+
var actionRec = null;
|
|
1504
|
+
for (var ai = 0; ai < messageRecords.length; ai++) {
|
|
1505
|
+
if (messageRecords[ai].id === data.recordId) { actionRec = messageRecords[ai]; break; }
|
|
1506
|
+
}
|
|
1507
|
+
if (actionRec && actionRec.kind === "question" && !actionRec.decision) {
|
|
1508
|
+
if (actionRec.buildConsentGate && (data.action === "proceed" || data.action === "skip")) {
|
|
1509
|
+
actionRec.decision = data.action;
|
|
1510
|
+
runBuildConsentDecision(actionRec, data.action === "proceed");
|
|
1511
|
+
} else if (actionRec.agentToolConsent && (data.action === "proceed" || data.action === "skip")) {
|
|
1512
|
+
actionRec.decision = data.action;
|
|
1513
|
+
if (typeof actionRec.onResume === "function") { actionRec.onResume(data.action === "proceed"); }
|
|
1514
|
+
} else if (actionRec.askUserTool && data.action === "answer") {
|
|
1515
|
+
actionRec.decision = "answered";
|
|
1516
|
+
actionRec.answerText = String(data.value || "");
|
|
1517
|
+
if (typeof actionRec.onAnswer === "function") { actionRec.onAnswer(actionRec.answerText); }
|
|
1518
|
+
} else if (actionRec.loopCheckpoint && (data.action === "continue" || data.action === "stop")) {
|
|
1519
|
+
actionRec.decision = data.action;
|
|
1520
|
+
if (typeof actionRec.onResume === "function") { actionRec.onResume(data.action); }
|
|
1521
|
+
}
|
|
1522
|
+
} else if (actionRec && actionRec.kind === "chip" && actionRec.chipType === "suggestedAction"
|
|
1523
|
+
&& data.action === "apply-suggested-action") {
|
|
1524
|
+
// CLAUDE-028: the redirect chip's own click handler (bound
|
|
1525
|
+
// directly on the button at render time) doesn't survive the
|
|
1526
|
+
// pop-out's innerHTML clone. Rides the same rebind-by-record-id
|
|
1527
|
+
// relay renderAskUserQuestion's quick-reply buttons already use
|
|
1528
|
+
// (bindRecordActionButtons in this file) — no chip-specific wiring
|
|
1529
|
+
// needed on the pop-out side.
|
|
1530
|
+
applySuggestedAction(actionRec.suggestedAction);
|
|
1531
|
+
}
|
|
1371
1532
|
} else if (data.event === "stopBuildLoop") {
|
|
1372
1533
|
stopBuildLoop("Build loop stopped — applied nodes remain as-is.");
|
|
1373
1534
|
} else if (data.event === "clearChat") {
|
|
@@ -1438,8 +1599,49 @@
|
|
|
1438
1599
|
});
|
|
1439
1600
|
|
|
1440
1601
|
loadSettings();
|
|
1602
|
+
checkForFlowPilotUpdates();
|
|
1441
1603
|
updateSelectionStatus();
|
|
1442
1604
|
showChat();
|
|
1605
|
+
rehydrateConversationOnLoad();
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// CLAUDE-029: on a page reload, sessionStorage already retains the
|
|
1609
|
+
// conversation-id (see history.js) and the backend already has the full
|
|
1610
|
+
// transcript (History panel proves it's retrievable) — but nothing ever
|
|
1611
|
+
// fetched and rendered it into the Chat panel on init, so a reload left
|
|
1612
|
+
// Chat looking empty even though the conversation was never lost.
|
|
1613
|
+
// Reuses loadConversation() verbatim (same fetch + same addMessage()
|
|
1614
|
+
// render path the History panel's click handler uses) so there is no
|
|
1615
|
+
// second copy of the message-rendering logic to drift out of sync.
|
|
1616
|
+
function rehydrateConversationOnLoad() {
|
|
1617
|
+
flowpilotStorageLog("log", "rehydrateConversationOnLoad-enter", {
|
|
1618
|
+
conversationIdWasRestored: conversationIdWasRestored,
|
|
1619
|
+
conversationId: conversationId,
|
|
1620
|
+
href: location.href
|
|
1621
|
+
});
|
|
1622
|
+
if (!conversationIdWasRestored) { return; }
|
|
1623
|
+
loadConversation(conversationId, function (msg, xhr) {
|
|
1624
|
+
// Conversation no longer exists server-side (404) — the
|
|
1625
|
+
// sessionStorage entry is stale, so drop it rather than retry
|
|
1626
|
+
// this same failed fetch on every future reload. Any other
|
|
1627
|
+
// failure (network hiccup, etc.) leaves it in place in case it
|
|
1628
|
+
// was transient. Either way: stay quiet, Chat simply stays
|
|
1629
|
+
// empty exactly as it does today — no error bubble on load.
|
|
1630
|
+
flowpilotStorageLog("warn", "rehydrateConversationOnLoad-error", {
|
|
1631
|
+
conversationId: conversationId,
|
|
1632
|
+
status: xhr && xhr.status,
|
|
1633
|
+
message: msg
|
|
1634
|
+
});
|
|
1635
|
+
if (xhr && xhr.status === 404) {
|
|
1636
|
+
clearConversationId("rehydrate 404");
|
|
1637
|
+
clearRunMarker("rehydrate 404");
|
|
1638
|
+
}
|
|
1639
|
+
}, function () {
|
|
1640
|
+
flowpilotStorageLog("log", "rehydrateConversationOnLoad-success", {
|
|
1641
|
+
conversationId: conversationId
|
|
1642
|
+
});
|
|
1643
|
+
restoreInterruptedRunMarker(conversationId);
|
|
1644
|
+
}, { preserveRunMarker: true });
|
|
1443
1645
|
}
|
|
1444
1646
|
|
|
1445
1647
|
window.FlowPilotCore = { initMainWindow: initMainWindow, initPopout: initPopout };
|