Profile permissions belong in one table, because scattered they hid a capability chat was never meant to have
This commit is contained in:
@@ -92,6 +92,11 @@ if (!preg_match('#/[0-9a-f]{32}\.json$#', $jobFile)) exit(1);
|
||||
|
||||
$profile = in_array($profile, ['varaverk', 'chat', 'code', 'troubleshoot'], true) ? $profile : 'varaverk';
|
||||
|
||||
// Every "is this profile allowed X" question in this file goes through here. Written as a closure
|
||||
// over $profile so no call site can accidentally ask about a different one, which is the shape the
|
||||
// old scattered comparisons kept taking.
|
||||
$can = fn(string $cap): bool => vv_ai_profile_can($profile, $cap);
|
||||
|
||||
function jw(string $f, array $d): void {
|
||||
file_put_contents($f, json_encode($d));
|
||||
}
|
||||
@@ -114,7 +119,7 @@ $sources = [];
|
||||
$context = '';
|
||||
$tRetrieve = 0.0;
|
||||
|
||||
if ($profile === 'varaverk' || $profile === 'troubleshoot') {
|
||||
if ($can('retrieve')) {
|
||||
jw($jobFile, ['status' => 'retrieving']);
|
||||
|
||||
// A definitional question with no explicit filter goes to the narrative docs. Left alone,
|
||||
@@ -160,18 +165,16 @@ if ($profile === 'varaverk' || $profile === 'troubleshoot') {
|
||||
// — nothing had failed, so nothing in the question said failure. It retrieved the documentation
|
||||
// on where logs live and answered with directions to a page the operator already had open. The
|
||||
// question is about a run that happened, so the run itself has to be in context.
|
||||
//
|
||||
// Restricted to the two profiles that are allowed Varaverk's own material. chat is promised, as
|
||||
// its first and most absolute rule, that it has not been shown this installation — attaching a
|
||||
// log tail to it because the phrasing matched would break exactly the guarantee that stops it
|
||||
// inventing confident answers about the operator's system.
|
||||
$runOutcome = ($profile === 'varaverk' || $profile === 'troubleshoot') && (bool)preg_match(
|
||||
$runOutcome = $can('run_evidence') && (bool)preg_match(
|
||||
'/\b(how did|how.d|did .{0,24}\b(run|go|finish|complete)|last run|latest run|last night|'
|
||||
. 'go last|went last|how long did|run record|rundown|summar(y|ise|ize)|recap)\b/i',
|
||||
$question
|
||||
);
|
||||
|
||||
$diagnostic = $profile === 'troubleshoot' || $runOutcome || ($profile === 'varaverk' && (bool)preg_match(
|
||||
// Permission first, need second: the capability decides whether live state may be attached at
|
||||
// all, and only then does the phrasing decide whether this particular question warrants it.
|
||||
// troubleshoot needs no phrasing test — the operator opened a log to get there.
|
||||
$diagnostic = $can('health') && ($profile === 'troubleshoot' || $runOutcome || (bool)preg_match(
|
||||
'/\b(why|fail(ed|ing|ure)?|error|broken?|not work|isn.t work|wrong|stuck|hang|'
|
||||
. 'never runs?|didn.t|won.t|debug|troubleshoot|diagnos)/i',
|
||||
$question
|
||||
@@ -206,7 +209,7 @@ if ($diagnostic) {
|
||||
// question. The second half is what makes a run-outcome question work from any view: asking how
|
||||
// the daily orchestrator went while looking at the suggestions list is the ordinary case, not an
|
||||
// edge one, and requiring them to open the log first is asking them to do the lookup themselves.
|
||||
$runTarget = ($profile === 'troubleshoot' && $scope !== '') ? $scope : '';
|
||||
$runTarget = ($can('scoped_log') && $scope !== '') ? $scope : '';
|
||||
if ($runTarget === '' && $runOutcome) $runTarget = vv_ai_resolve_run_target($question);
|
||||
|
||||
$scopedLog = null;
|
||||
@@ -241,7 +244,10 @@ if ($runTarget !== '' && vv_ai_scope_ok($runTarget)) {
|
||||
// What has gone wrong with this same thing before, and what actually fixed it. Operator-written,
|
||||
// so it outranks anything the model would infer from the log — it is the only input here that
|
||||
// records a confirmed outcome rather than a reading of evidence.
|
||||
if ($scope !== '') {
|
||||
// Gated on the capability, which it was not before: this block keyed only on a scope being
|
||||
// present, so General Chat opened against a script was handed the operator's own incident notes
|
||||
// about it — the same leak as the log, one block further down.
|
||||
if ($can('incidents') && $scope !== '') {
|
||||
$past = vv_ai_incidents_for($scope, 4);
|
||||
if ($past) {
|
||||
$diagBlock .= "PREVIOUSLY ON THIS, WRITTEN BY THE OPERATOR AFTER IT WAS RESOLVED\n"
|
||||
@@ -255,7 +261,7 @@ if ($scope !== '') {
|
||||
// Where a named conf key really lives, resolved before the model sees the question. Deterministic
|
||||
// so the answer cannot be a guess: the operator may be certain a setting is in master.conf when
|
||||
// it is in the host conf, and the useful reply names the file and line rather than not finding it.
|
||||
if ($profile === 'varaverk' || $profile === 'troubleshoot') {
|
||||
if ($can('conf_lookup')) {
|
||||
$seen = [];
|
||||
if (preg_match_all('/\b([A-Z][A-Z0-9_]{4,})\b/', $question, $km)) {
|
||||
foreach (array_slice(array_unique($km[1]), 0, 4) as $k) {
|
||||
@@ -472,9 +478,7 @@ if ($diagBlock !== '') {
|
||||
// the instruction is repeated immediately before the user's message, where it is hardest to
|
||||
// ignore. Detection only ever makes the model MORE cautious, so a false positive costs a
|
||||
// redirect rather than a wrong answer.
|
||||
if ($profile === 'chat'
|
||||
&& (vv_ai_mentions_varaverk($question)
|
||||
|| preg_match('/\b[\w.-]+\.sh\b|\b[A-Z][A-Z0-9]*(_[A-Z0-9]+)+\b/', $question))) {
|
||||
if ($profile === 'chat' && vv_ai_chat_needs_varaverk($question)) {
|
||||
$system .= "NOTE: the operator's message appears to name a specific Varaverk component. "
|
||||
. "You cannot see the documentation in this mode, so you do not know what it does. "
|
||||
. "Say that plainly, point them at the Varaverk Assistant profile, and do not "
|
||||
@@ -543,7 +547,7 @@ if ($answer === '') {
|
||||
// expensive mistake is not tangled logic — it is a simple script pointed one directory too high.
|
||||
// Scans only fenced code, so prose mentioning "rm" does not trip it.
|
||||
$warnings = [];
|
||||
if ($profile === 'code' && preg_match_all('/```(?:\w+)?\n(.*?)```/s', $answer, $blocks)) {
|
||||
if ($can('code_scan') && preg_match_all('/```(?:\w+)?\n(.*?)```/s', $answer, $blocks)) {
|
||||
$code = implode("\n", $blocks[1]);
|
||||
$checks = [
|
||||
'/(^|[;&|\s])rm\s+(-[a-zA-Z]*\s+)*/m' => 'deletes files (rm)',
|
||||
@@ -569,7 +573,7 @@ if ($profile === 'code' && preg_match_all('/```(?:\w+)?\n(.*?)```/s', $answer, $
|
||||
// the guard lives here, in code, not in the instruction that asked for it. A prompt is a
|
||||
// request; this is the part that decides.
|
||||
$bugFiled = null;
|
||||
if ($profile === 'troubleshoot'
|
||||
if ($can('file_bugs')
|
||||
&& preg_match('/\[VARAVERK-BUG\](.*?)\[\/VARAVERK-BUG\]/s', $answer, $bm)) {
|
||||
$answer = trim(preg_replace('/\[VARAVERK-BUG\].*?\[\/VARAVERK-BUG\]/s', '', $answer));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user