fix: bound workflow keyword matching - #79
Conversation
9c8171a to
b3d280c
Compare
|
Thanks @SSS135 — clean first split, and I appreciate you rebasing onto 2.14.0 already. Reviewed closely: the Unicode identifier-boundary regex is correct and ReDoS-safe (fixed-length lookarounds + a literal match, no nested quantifiers), default-on keyword triggering is genuinely preserved (only a new test locks it in — the Merging. Looking forward to the rest as you mark them ready — |
…, #89) (#93) * feat(workflow): authorize keyword triggering instead of forcing it Reframes keyword/effort triggering from 'force a workflow' to 'arm the tool and let the model decide', addressing #88 (over-triggering), #89 (triggered runs going idle instead of staying interactive), and cutting the always-on prompt (part of #65). - buildForcedWorkflowPrompt -> buildArmedWorkflowPrompt: the trigger now injects an authorize directive ('typing the word is explicit opt-in; if it's a request call workflow, if it's a question just answer -- the opt-in does not force a workflow') instead of 'You MUST / the ONLY acceptable action / Do NOT answer'. A lexical false-positive is no longer amplified into a forced wrong action. - #89 falls out of de-forcing: the idle was a downstream effect of the model being told to emit nothing but one bare background workflow call (which ends the turn). Handled in a normal turn, Pi stays interactive and the model can fold results inline. No change to the tool's background machinery. - Always-on prompt shrunk ~91% (6500 -> ~600 bytes): the ~20 'For workflow,' how-to lines moved off the always-on promptGuidelines into workflowHowToGuidelines(), injected into the message only on an armed turn; the always-on guideline is now a single gate line. Cuts self-priming and the always-on token cost, and stops churning the prompt-prefix cache. - byEffort path de-forced too, with an explicit conversational-turn escape. - Kept: keywordTriggerEnabled default ON (under authorize semantics the worst case is just an armed-turn directive), the #79 boundary regex, rainbow highlight, Backspace-to-disarm. README updated (force -> arm). 914 tests; always-on prompt budget ratcheted 6500 -> 650. * refactor(workflow): root-and-branch authorize fix (P1-P5) - Move the how-to mechanics from the per-armed-turn message into the workflow tool's static description, so the model has the manual on every arming path AND on off-keyword natural-language opt-ins, cacheably, not as per-turn priming (P2). Always-on promptGuidelines stays the single gate line. - buildArmedWorkflowPrompt now leads with the decision boundary, states the truthful opt-in reason per path (keyword vs standing-effort), and carries the #89 background/deliver-back reassurance so an ending turn reads as expected, not a stall (P1, P3). - /workflows run uses a distinct forcing directive (no question-escape) while avoiding the old MUST/ONLY language (P5). - Gate line gains task-shape positives, balancing the strong negative (P4). - Tests: trigger regression corpus (negatives/positives), gate-line R3, how-to now in tool description, armed-directive content, /workflows run forcing; honest prompt-budget ratchets (always-on ~766B, tool def grows to ~8.8KB as a MOVE, armed message drops ~6.8KB -> ~0.9KB; how-to trimming is #65, separate). * docs: note the off-keyword natural-language path needs the workflow tool default-active The how-to now lives in the tool description, so a bare natural-language opt-in ('fan this out') sees the mechanics only if the host keeps the workflow tool in its default active set. The arming paths add it on arm; document the dependency.
Summary
Adds Unicode-aware identifier boundaries for workflow keyword matching while preserving the existing default-on auto-trigger behavior.
Windows test portability
Full Windows validation identified three test-harness portability issues on current
main: home-directory isolation acrossHOMEandUSERPROFILE, platform-aware expected paths withnode:path.join, and file-URL conversion for absolute dynamic-import paths. The corrections are confined to test setup and path handling and preserve the existing assertions.Validation
npm test: 899/899 passedgit diff --check: passedSplit from and supersedes the keyword-matching portion of #74.