atris 3.58.7 → 3.58.8
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/GETTING_STARTED.md +2 -2
- package/PERSONA.md +11 -11
- package/README.md +1 -1
- package/atris/GEMINI.md +7 -6
- package/atris/PERSONA.md +4 -4
- package/atris/features/README.md +77 -6
- package/atris/skills/README.md +44 -15
- package/atris/skills/aeo/SKILL.md +10 -10
- package/atris/skills/atris/SKILL.md +1 -1
- package/atris/skills/engines/SKILL.md +42 -50
- package/atris/skills/meta/SKILL.md +4 -4
- package/bin/atris.js +5 -6
- package/commands/aeo.js +9 -12
- package/commands/analytics.js +8 -11
- package/commands/autopilot.js +4 -3
- package/commands/avail.js +1 -1
- package/commands/brain.js +3 -1
- package/commands/business.js +13 -8
- package/commands/close.js +2 -1
- package/commands/computer.js +15 -10
- package/commands/context-sync.js +3 -3
- package/commands/doc-health.js +50 -2
- package/commands/experiments.js +13 -4
- package/commands/improve.js +1 -1
- package/commands/integrations.js +2 -5
- package/commands/learn.js +1 -1
- package/commands/member.js +174 -47
- package/commands/mission.js +57 -11
- package/commands/pack.js +2 -2
- package/commands/probe.js +11 -5
- package/commands/pull.js +1 -1
- package/commands/push.js +1 -1
- package/commands/rainmaker.js +3 -16
- package/commands/spaceship.js +1 -1
- package/commands/stream.js +27 -2
- package/commands/study.js +15 -9
- package/commands/sync.js +1 -1
- package/commands/task.js +13 -2
- package/commands/workflow.js +6 -1
- package/lib/codex-flight.js +28 -5
- package/lib/fleet.js +4 -1
- package/lib/improve-vitals-html.js +3 -2
- package/lib/lesson-contradiction.js +2 -2
- package/lib/map-refs.js +183 -0
- package/lib/mission-room.js +4 -4
- package/lib/noninteractive.js +15 -4
- package/lib/policy-lessons.js +1 -1
- package/lib/receipt-block.js +137 -28
- package/lib/runner-command.js +1 -1
- package/lib/scorecard.js +4 -4
- package/lib/sleep-sync.js +11 -0
- package/lib/state-detection.js +28 -9
- package/lib/task-db.js +3 -2
- package/lib/todo-fallback.js +1 -1
- package/lib/wish-delegate.js +19 -5
- package/mcp/atris-mcp/index.mjs +77 -24
- package/package.json +2 -5
- package/scripts/det/det.js +2 -2
- package/scripts/det/pr-description.js +2 -2
- package/scripts/det/test.js +2 -2
- package/utils/api.js +1 -1
- package/utils/auth.js +17 -7
- package/utils/backend-root.js +31 -0
- package/utils/config.js +5 -4
- package/utils/owner-identity.js +61 -0
- package/atris/policies/LESSONS.md +0 -13
- package/atris/skills/autopilot/atris-autopilot.md +0 -71
package/commands/member.js
CHANGED
|
@@ -4,10 +4,12 @@ const crypto = require('crypto');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const { execFileSync, spawnSync } = require('child_process');
|
|
6
6
|
const { loadCredentials } = require('../utils/auth');
|
|
7
|
+
const { resolveBackendRoot } = require('../utils/backend-root');
|
|
7
8
|
const { apiRequestJson } = require('../utils/api');
|
|
8
9
|
const { runAliveTick } = require('../lib/member-alive');
|
|
9
10
|
const { defaultObjectiveRunner } = require('../lib/default-runner');
|
|
10
11
|
const { readJson, writeJson } = require('../lib/json-file');
|
|
12
|
+
const { sleepSync } = require('../lib/sleep-sync');
|
|
11
13
|
const { hasFlag, readFlag, readNumberFlag } = require('../lib/arg-parser');
|
|
12
14
|
const { ensureMemberBundle, memberBundlePresent } = require('../lib/member-scaffold');
|
|
13
15
|
const { memberProcessPrompt, MEMBER_PROCESS_PATH } = require('../lib/member-context');
|
|
@@ -686,6 +688,15 @@ const MEMBER_RUN_START_VALUE_FLAGS = [
|
|
|
686
688
|
'--mission-id',
|
|
687
689
|
'--owner',
|
|
688
690
|
'--runner',
|
|
691
|
+
'--engine',
|
|
692
|
+
'--budget',
|
|
693
|
+
'--repo',
|
|
694
|
+
'--native-goal-status',
|
|
695
|
+
'--native-goal-objective',
|
|
696
|
+
'--visible-goal-status',
|
|
697
|
+
'--visible-goal-objective',
|
|
698
|
+
'--max-idle-ticks',
|
|
699
|
+
'--slots',
|
|
689
700
|
'--lane',
|
|
690
701
|
'--cadence',
|
|
691
702
|
'--verify',
|
|
@@ -717,10 +728,49 @@ const MEMBER_RUN_START_BOOLEAN_FLAGS = [
|
|
|
717
728
|
'--stop-when-done',
|
|
718
729
|
'--choose-work',
|
|
719
730
|
'--auto',
|
|
731
|
+
'--headless',
|
|
732
|
+
'--self-drive',
|
|
733
|
+
'--no-claude',
|
|
734
|
+
'--complete-on-pass',
|
|
735
|
+
'--no-drain',
|
|
736
|
+
'--create-next',
|
|
737
|
+
'--preflight',
|
|
738
|
+
'--no-preflight',
|
|
739
|
+
'--room-preflight',
|
|
740
|
+
'--no-room-preflight',
|
|
741
|
+
'--room-auto-run',
|
|
742
|
+
'--no-room-auto-run',
|
|
743
|
+
'--manual-ack',
|
|
744
|
+
'--allow-native-goal-supersede',
|
|
745
|
+
'--supersede-paused-native-goal',
|
|
746
|
+
'--take-goal-slot',
|
|
747
|
+
'--due',
|
|
748
|
+
'--detach',
|
|
749
|
+
'--fleet',
|
|
750
|
+
'--dry-run',
|
|
751
|
+
'--land',
|
|
752
|
+
'--cloud',
|
|
720
753
|
];
|
|
721
754
|
|
|
755
|
+
function memberRunMissionWordIndices(args = []) {
|
|
756
|
+
const values = new Set(MEMBER_RUN_START_VALUE_FLAGS);
|
|
757
|
+
const booleans = new Set(['--full', '--verbose', ...MEMBER_RUN_START_BOOLEAN_FLAGS]);
|
|
758
|
+
const indices = [];
|
|
759
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
760
|
+
const arg = String(args[i]);
|
|
761
|
+
const key = arg.includes('=') ? arg.slice(0, arg.indexOf('=')) : arg;
|
|
762
|
+
if (booleans.has(key)) continue;
|
|
763
|
+
if (values.has(key)) {
|
|
764
|
+
if (!arg.includes('=') && args[i + 1] && !String(args[i + 1]).startsWith('--')) i += 1;
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
indices.push(i);
|
|
768
|
+
}
|
|
769
|
+
return indices;
|
|
770
|
+
}
|
|
771
|
+
|
|
722
772
|
function memberRunMissionText(args = []) {
|
|
723
|
-
return
|
|
773
|
+
return memberRunMissionWordIndices(args).map((index) => args[index]).join(' ').trim();
|
|
724
774
|
}
|
|
725
775
|
|
|
726
776
|
function cleanMemberRunPhrase(value) {
|
|
@@ -834,10 +884,8 @@ function buildMemberRunStartArgs(owner, missionText, args = [], cwd = process.cw
|
|
|
834
884
|
pushFlagValue(startArgs, args, '--minutes');
|
|
835
885
|
pushFlagValue(startArgs, args, '--hours');
|
|
836
886
|
pushFlagValue(startArgs, args, '--base');
|
|
837
|
-
//
|
|
838
|
-
//
|
|
839
|
-
// wishes) would only kill autonomous legs like autopilot's "member chooses
|
|
840
|
-
// useful work". When the caller named no verifier, opt out explicitly.
|
|
887
|
+
// Member runs choose and drive work immediately, so an absent check must not
|
|
888
|
+
// leave autonomous callers with a mission that never ran.
|
|
841
889
|
if (hasFlag(args, '--no-verify') || !readFlag(args, '--verify', '')) {
|
|
842
890
|
startArgs.push('--no-verify');
|
|
843
891
|
}
|
|
@@ -853,7 +901,7 @@ function buildMemberRunStartArgs(owner, missionText, args = [], cwd = process.cw
|
|
|
853
901
|
if (!hasFlag(args, '--shared-checkout') && !hasFlag(args, '--no-worktree') && insideGitRepo(cwd)) {
|
|
854
902
|
startArgs.push('--worktree');
|
|
855
903
|
}
|
|
856
|
-
|
|
904
|
+
startArgs.push('--json');
|
|
857
905
|
return startArgs;
|
|
858
906
|
}
|
|
859
907
|
|
|
@@ -871,15 +919,52 @@ function startMemberRunMission(name, missionText, args = []) {
|
|
|
871
919
|
const paths = requireMemberDir(name);
|
|
872
920
|
const owner = paths.storageName || name;
|
|
873
921
|
const startArgs = buildMemberRunStartArgs(owner, missionText, args);
|
|
922
|
+
return memberRunCli(startArgs, process.cwd());
|
|
923
|
+
}
|
|
874
924
|
|
|
925
|
+
function memberRunCli(args, cwd, { live = false, missionId = '' } = {}) {
|
|
875
926
|
const cliPath = path.join(__dirname, '..', 'bin', 'atris.js');
|
|
927
|
+
const runsDir = path.join(cwd, 'atris', 'runs');
|
|
928
|
+
const previousReceipts = live && fs.existsSync(runsDir) ? new Set(fs.readdirSync(runsDir)) : new Set();
|
|
929
|
+
const result = spawnSync(process.execPath, [cliPath, ...args], {
|
|
930
|
+
cwd,
|
|
931
|
+
encoding: 'utf8',
|
|
932
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
933
|
+
stdio: live ? 'inherit' : ['ignore', 'pipe', 'pipe'],
|
|
934
|
+
});
|
|
935
|
+
if (result.error) throw result.error;
|
|
936
|
+
if (live) {
|
|
937
|
+
const receiptName = fs.existsSync(runsDir)
|
|
938
|
+
? fs.readdirSync(runsDir).filter((name) => !previousReceipts.has(name) && name.startsWith(`mission-${missionId}-`) && name.endsWith('.json')).sort().at(-1)
|
|
939
|
+
: null;
|
|
940
|
+
const receipt = receiptName ? readJson(path.join(runsDir, receiptName), null) : null;
|
|
941
|
+
return { status: result.status, payload: receipt?.result?.kind === 'mission_run_summary'
|
|
942
|
+
? { ran_ticks: receipt.result.ran_ticks } : null, stderr: '', errorReported: result.status !== 0 };
|
|
943
|
+
}
|
|
944
|
+
let payload = null;
|
|
876
945
|
try {
|
|
877
|
-
|
|
878
|
-
cwd: process.cwd(),
|
|
879
|
-
stdio: 'inherit',
|
|
880
|
-
});
|
|
946
|
+
payload = JSON.parse(result.stdout);
|
|
881
947
|
} catch (error) {
|
|
882
|
-
|
|
948
|
+
if (result.status === 0) throw new Error(`member run: mission command returned invalid json: ${error.message}`);
|
|
949
|
+
}
|
|
950
|
+
return { status: result.status, payload, stderr: String(result.stderr || '').trim() };
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function memberRunConfiguredVerifier(paths) {
|
|
954
|
+
const frontmatter = parseFrontmatter(fs.readFileSync(paths.memberFile, 'utf8')) || {};
|
|
955
|
+
const check = frontmatter.verify || frontmatter.verifier;
|
|
956
|
+
return typeof check === 'string' ? check.trim() : '';
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function printMemberRunResult(result, asJson) {
|
|
960
|
+
if (asJson) {
|
|
961
|
+
console.log(JSON.stringify(result));
|
|
962
|
+
} else if (result.started) {
|
|
963
|
+
console.log(`started ${result.mission_id} (${result.state})`);
|
|
964
|
+
}
|
|
965
|
+
if (result.ok === false || (!result.started && result.ok !== true)) {
|
|
966
|
+
if (!result.errorReported) console.error(result.error);
|
|
967
|
+
process.exitCode = 1;
|
|
883
968
|
}
|
|
884
969
|
}
|
|
885
970
|
|
|
@@ -967,48 +1052,96 @@ function memberPing(name, ...args) {
|
|
|
967
1052
|
|
|
968
1053
|
function memberRun(name, ...args) {
|
|
969
1054
|
if (!name || name === '--help' || name === '-h' || hasFlag(args, '--help') || hasFlag(args, '-h')) {
|
|
970
|
-
console.log('
|
|
971
|
-
console.log('
|
|
972
|
-
console.log('
|
|
973
|
-
console.log('
|
|
974
|
-
console.log('
|
|
975
|
-
console.log('
|
|
976
|
-
console.log('
|
|
1055
|
+
console.log('usage: atris member run <name> ["mission text"] [--verify "<cmd>"] [--minutes N|--hours N] [--json]');
|
|
1056
|
+
console.log('new mission: atris member run growth "improve onboarding proof" --verify "npm test" --minutes 30 --json');
|
|
1057
|
+
console.log('choose work: atris member run growth --verify "npm test" --industry logistics --minutes 30 --json');
|
|
1058
|
+
console.log('existing mission: atris member run growth --mission <mission-id> --max-ticks 1 --json');
|
|
1059
|
+
console.log('meaning: if mission text is omitted, the member chooses one useful bounded task from atris state.');
|
|
1060
|
+
console.log('truth: useful work must improve revenue, reliability, speed, security, clarity, or trust, then show plain proof.');
|
|
1061
|
+
console.log('isolation: new missions use --worktree by default; add --shared-checkout to stay here.');
|
|
1062
|
+
console.log('check: --verify or a verify field in the member file adds a mission check.');
|
|
977
1063
|
return;
|
|
978
1064
|
}
|
|
979
1065
|
|
|
1066
|
+
const asJson = hasFlag(args, '--json');
|
|
1067
|
+
const paths = requireMemberDir(name);
|
|
1068
|
+
const requestedVerifier = readFlag(args, '--verify', '') || memberRunConfiguredVerifier(paths);
|
|
980
1069
|
const missionText = memberRunMissionText(args);
|
|
981
1070
|
const hasMissionOverride = Boolean(readFlag(args, '--mission', '') || readFlag(args, '--mission-id', ''));
|
|
1071
|
+
let mission = null;
|
|
1072
|
+
let startedNewMission = false;
|
|
1073
|
+
let nodeModulesLinked = false;
|
|
1074
|
+
let nodeModulesSource = null;
|
|
982
1075
|
if (missionText && !hasMissionOverride) {
|
|
983
|
-
startMemberRunMission(name, missionText, args);
|
|
984
|
-
|
|
1076
|
+
const start = startMemberRunMission(name, missionText, requestedVerifier ? [...args, '--verify', requestedVerifier] : args);
|
|
1077
|
+
if (start.status !== 0 || !start.payload?.mission) {
|
|
1078
|
+
printMemberRunResult({ started: false, state: null, mission_id: start.payload?.mission?.id || null,
|
|
1079
|
+
error: start.payload?.detail || start.payload?.error || start.stderr || 'mission creation failed' }, asJson);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
mission = start.payload.mission;
|
|
1083
|
+
startedNewMission = true;
|
|
1084
|
+
nodeModulesLinked = Boolean(start.payload.node_modules_linked);
|
|
1085
|
+
nodeModulesSource = start.payload.node_modules_source || null;
|
|
1086
|
+
} else {
|
|
1087
|
+
const missionId = resolveMemberRunMissionId(name, args);
|
|
1088
|
+
if (!missionId) {
|
|
1089
|
+
const start = startMemberRunMission(name, memberRunAutoMissionText(name, args), requestedVerifier ? [...args, '--verify', requestedVerifier] : args);
|
|
1090
|
+
if (start.status !== 0 || !start.payload?.mission) {
|
|
1091
|
+
printMemberRunResult({ started: false, state: null, mission_id: start.payload?.mission?.id || null,
|
|
1092
|
+
error: start.payload?.detail || start.payload?.error || start.stderr || 'mission creation failed' }, asJson);
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
mission = start.payload.mission;
|
|
1096
|
+
startedNewMission = true;
|
|
1097
|
+
nodeModulesLinked = Boolean(start.payload.node_modules_linked);
|
|
1098
|
+
nodeModulesSource = start.payload.node_modules_source || null;
|
|
1099
|
+
} else {
|
|
1100
|
+
mission = require('./mission').resolveMission(missionId);
|
|
1101
|
+
if (!mission) {
|
|
1102
|
+
printMemberRunResult({ started: false, state: null, mission_id: missionId, error: `mission ${missionId} was not found` }, asJson);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
985
1106
|
}
|
|
986
1107
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1108
|
+
if (startedNewMission && !asJson) {
|
|
1109
|
+
if (mission.worktree?.path) console.log(`worktree: ${mission.worktree.path}`);
|
|
1110
|
+
if (nodeModulesLinked) console.log(`linked node_modules from ${nodeModulesSource}`);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
const cwd = mission.worktree?.path || process.cwd();
|
|
1114
|
+
const verifier = readFlag(args, '--verify', '') || mission.verifier || requestedVerifier;
|
|
1115
|
+
if (verifier && verifier !== mission.verifier) {
|
|
1116
|
+
const changed = memberRunCli(['mission', 'set-verifier', mission.id, verifier, '--json'], cwd);
|
|
1117
|
+
if (changed.status !== 0) {
|
|
1118
|
+
printMemberRunResult({ started: false, state: mission.status, mission_id: mission.id, mission,
|
|
1119
|
+
error: changed.payload?.detail || changed.payload?.error || changed.stderr || 'could not set mission check' }, asJson);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
mission.verifier = verifier;
|
|
991
1123
|
}
|
|
992
1124
|
|
|
993
|
-
const
|
|
1125
|
+
const missionWordIndices = missionText && !hasMissionOverride ? new Set(memberRunMissionWordIndices(args)) : new Set();
|
|
1126
|
+
const executionArgs = args.filter((_, index) => !missionWordIndices.has(index));
|
|
1127
|
+
const runArgs = stripKnownFlags(executionArgs,
|
|
1128
|
+
['--mission', '--mission-id', '--minutes', '--hours', '--industry', '--domain', '--value', '--outcome', '--truth', '--proof', '--base'],
|
|
1129
|
+
['--worktree', '--shared-checkout', '--no-worktree', '--choose-work', '--auto']);
|
|
994
1130
|
const budgetSeconds = memberRunBudgetSeconds(args);
|
|
995
|
-
// A timed run is a loop contract, not one tick: keep picking the next useful
|
|
996
|
-
// move until the wall clock (--max-wall) spends the budget. Only untimed runs
|
|
997
|
-
// default to a single tick.
|
|
998
1131
|
if (!readFlag(runArgs, '--max-ticks', '')) {
|
|
999
1132
|
runArgs.push('--max-ticks', budgetSeconds ? String(Math.max(4, Math.ceil(budgetSeconds / 300))) : '1');
|
|
1000
1133
|
}
|
|
1001
1134
|
if (!readFlag(runArgs, '--max-wall', '')) runArgs.push('--max-wall', String(budgetSeconds || 900));
|
|
1002
|
-
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1135
|
+
const run = memberRunCli(['mission', 'run', mission.id, ...runArgs], cwd, { live: !asJson, missionId: mission.id });
|
|
1136
|
+
const payload = run.payload || {};
|
|
1137
|
+
const finalMission = payload.mission || require('./mission').resolveMission(mission.id, cwd) || mission;
|
|
1138
|
+
const started = run.status === 0 && payload.ok !== false && Number(payload.ran_ticks) > 0;
|
|
1139
|
+
const noWorkStepExpected = hasFlag(runArgs, '--detach') || hasFlag(runArgs, '--dry-run');
|
|
1140
|
+
const ok = run.status === 0 && payload.ok !== false && (started || noWorkStepExpected);
|
|
1141
|
+
printMemberRunResult({ ...payload, ok, started, state: finalMission.status, mission_id: mission.id,
|
|
1142
|
+
mission: finalMission, node_modules_linked: nodeModulesLinked, node_modules_source: nodeModulesSource,
|
|
1143
|
+
errorReported: run.errorReported,
|
|
1144
|
+
...(!ok ? { error: payload.detail || payload.error || payload.reason || run.stderr || 'mission did not run a work step' } : {}) }, asJson);
|
|
1012
1145
|
}
|
|
1013
1146
|
|
|
1014
1147
|
function loadTeamScoreEvidence(scoreJsonPath) {
|
|
@@ -1831,8 +1964,8 @@ function problemSignalSources(root = process.cwd()) {
|
|
|
1831
1964
|
sources.push(source);
|
|
1832
1965
|
}
|
|
1833
1966
|
for (const source of problemSignalFilesForStateDir(path.join(root, '.atris', 'state'))) push(source);
|
|
1834
|
-
const siblingBackend =
|
|
1835
|
-
if (
|
|
1967
|
+
const siblingBackend = resolveBackendRoot(root);
|
|
1968
|
+
if (siblingBackend) {
|
|
1836
1969
|
for (const source of problemSignalFilesForStateDir(path.join(siblingBackend, '.atris', 'state'), 'backend')) push(source);
|
|
1837
1970
|
}
|
|
1838
1971
|
for (const source of configuredProblemSignalSources(root)) push(source);
|
|
@@ -4594,12 +4727,6 @@ function printJsonOrText(payload, lines, asJson) {
|
|
|
4594
4727
|
for (const line of lines) console.log(line);
|
|
4595
4728
|
}
|
|
4596
4729
|
|
|
4597
|
-
function sleepSync(ms) {
|
|
4598
|
-
const duration = Math.max(0, Number(ms) || 0);
|
|
4599
|
-
if (duration <= 0) return;
|
|
4600
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, duration);
|
|
4601
|
-
}
|
|
4602
|
-
|
|
4603
4730
|
function memberGoal(name, ...args) {
|
|
4604
4731
|
const paths = requireMemberDir(name);
|
|
4605
4732
|
const asJson = hasFlag(args, '--json');
|
|
@@ -9261,7 +9388,7 @@ async function memberCommand(subcommand, ...args) {
|
|
|
9261
9388
|
console.log(' atris member goal-from-mission growth --json');
|
|
9262
9389
|
console.log(' atris member goal-from-score growth --score-json team-score.json --json');
|
|
9263
9390
|
console.log(' atris member wake growth --json');
|
|
9264
|
-
console.log(' atris member run growth "
|
|
9391
|
+
console.log(' atris member run growth "improve onboarding proof" --verify "npm test" --minutes 30 --json');
|
|
9265
9392
|
console.log(' atris member run growth --mission <mission-id> --max-ticks 1 --json');
|
|
9266
9393
|
console.log(' atris member wake growth --execute --confirm-autonomy-policy');
|
|
9267
9394
|
console.log(' atris member supervisor recommendations --json');
|
package/commands/mission.js
CHANGED
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
printCliJson,
|
|
14
14
|
} = require('../lib/cli-json');
|
|
15
15
|
const escapeRegExp = require('../lib/escape-regexp');
|
|
16
|
+
const { sleepSync } = require('../lib/sleep-sync');
|
|
16
17
|
const {
|
|
17
18
|
appendBriefRecord,
|
|
18
19
|
stampBriefOutcome,
|
|
@@ -3568,8 +3569,8 @@ function readTasteReviewHistory(root, limit = 4) {
|
|
|
3568
3569
|
|
|
3569
3570
|
function missionTasteMemorySignals(text) {
|
|
3570
3571
|
const definitions = [
|
|
3571
|
-
['plain_language', '
|
|
3572
|
-
['proof_gate', '
|
|
3572
|
+
['plain_language', 'the operator asks for plain English', /\b(plain|jargon|feynman|understand|simple|clarify|clarity)\b/i],
|
|
3573
|
+
['proof_gate', 'the operator wants proof before accept', /\b(proof|receipt|verify|verified|checked|tested|accept|approval)\b/i],
|
|
3573
3574
|
['runway_revenue', 'runway pushes user or revenue work', /\b(runway|revenue|cash|user|users|customer|demo|adoption|retention)\b/i],
|
|
3574
3575
|
['technical_ambition', 'technical advancement still matters', /\b(technical|homerun|research|runtime|architecture|model|benchmark|agent)\b/i],
|
|
3575
3576
|
['trust_boundary', 'trust and approval boundaries matter', /\b(security|safe|safety|trust|privacy|approval|permission|gmail|email|connector)\b/i],
|
|
@@ -3668,9 +3669,9 @@ function missionPlainTaskPreview(title) {
|
|
|
3668
3669
|
function missionTasteMemoryReason(tasteMemory, signals) {
|
|
3669
3670
|
const candidateIds = new Set((signals || []).map((signal) => signal.id));
|
|
3670
3671
|
const memoryIds = new Set((tasteMemory?.signals || []).map((signal) => signal.id));
|
|
3671
|
-
if (memoryIds.has('runway_revenue') && candidateIds.has('users_revenue')) return '
|
|
3672
|
-
if (memoryIds.has('plain_language') && candidateIds.has('usability')) return '
|
|
3673
|
-
if (memoryIds.has('proof_gate') && (candidateIds.has('trust') || candidateIds.has('technical'))) return '
|
|
3672
|
+
if (memoryIds.has('runway_revenue') && candidateIds.has('users_revenue')) return 'the operator has been steering toward user/revenue proof because runway is tight.';
|
|
3673
|
+
if (memoryIds.has('plain_language') && candidateIds.has('usability')) return 'the operator keeps asking for plain-English, understandable work.';
|
|
3674
|
+
if (memoryIds.has('proof_gate') && (candidateIds.has('trust') || candidateIds.has('technical'))) return 'the operator wants proof and receipts before serious work is accepted.';
|
|
3674
3675
|
if (memoryIds.has('working_memory')) return 'Recent logs are being used as working memory for what matters now.';
|
|
3675
3676
|
if (memoryIds.has('technical_ambition') && candidateIds.has('technical')) return 'The member memory still values technical advancement when it has proof.';
|
|
3676
3677
|
return '';
|
|
@@ -4160,14 +4161,61 @@ function startMission(args, options = {}) {
|
|
|
4160
4161
|
// checkout's dirt never reaches the mission baseline.
|
|
4161
4162
|
if (hasFlag(args, '--worktree')) {
|
|
4162
4163
|
let created;
|
|
4164
|
+
const sourceTop = spawnSync('git', ['rev-parse', '--show-toplevel'], { cwd: process.cwd(), encoding: 'utf8' });
|
|
4165
|
+
const sourceCheckout = sourceTop.status === 0 ? String(sourceTop.stdout).trim() : process.cwd();
|
|
4166
|
+
let nodeModulesSource = null;
|
|
4163
4167
|
try {
|
|
4164
4168
|
const { createAgentWorktree } = require('./worktree');
|
|
4165
4169
|
const base = readFlag(args, '--base', '') || inheritedWorktreeBase(process.cwd());
|
|
4166
4170
|
created = createAgentWorktree({ member: mission.owner, task: mission.objective, ...(base ? { base } : {}) });
|
|
4171
|
+
const packageFile = path.join(sourceCheckout, 'package.json');
|
|
4172
|
+
const sourceModules = path.join(sourceCheckout, 'node_modules');
|
|
4173
|
+
const targetModules = path.join(created.path, 'node_modules');
|
|
4174
|
+
try {
|
|
4175
|
+
let targetExists = false;
|
|
4176
|
+
try {
|
|
4177
|
+
fs.lstatSync(targetModules);
|
|
4178
|
+
targetExists = true;
|
|
4179
|
+
} catch (error) {
|
|
4180
|
+
if (error.code !== 'ENOENT') throw error;
|
|
4181
|
+
}
|
|
4182
|
+
if (fs.existsSync(packageFile) && fs.existsSync(sourceModules) && !targetExists) {
|
|
4183
|
+
const manifest = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
4184
|
+
const hasDependencies = ['dependencies', 'devDependencies', 'optionalDependencies']
|
|
4185
|
+
.some((field) => manifest[field] && Object.keys(manifest[field]).length > 0);
|
|
4186
|
+
if (hasDependencies) {
|
|
4187
|
+
const checkIgnored = () => {
|
|
4188
|
+
const result = spawnSync('git', ['check-ignore', '-q', 'node_modules'], { cwd: created.path, encoding: 'utf8' });
|
|
4189
|
+
if (result.error || (result.status !== 0 && result.status !== 1)) {
|
|
4190
|
+
throw new Error(result.error?.message || String(result.stderr || 'git check-ignore failed').trim());
|
|
4191
|
+
}
|
|
4192
|
+
return result.status === 0;
|
|
4193
|
+
};
|
|
4194
|
+
if (!checkIgnored()) {
|
|
4195
|
+
const commonDir = spawnSync('git', ['rev-parse', '--git-common-dir'], { cwd: created.path, encoding: 'utf8' });
|
|
4196
|
+
if (commonDir.error || commonDir.status !== 0) {
|
|
4197
|
+
throw new Error(commonDir.error?.message || String(commonDir.stderr || 'could not find shared git directory').trim());
|
|
4198
|
+
}
|
|
4199
|
+
const excludeFile = path.resolve(created.path, String(commonDir.stdout).trim(), 'info', 'exclude');
|
|
4200
|
+
fs.mkdirSync(path.dirname(excludeFile), { recursive: true });
|
|
4201
|
+
const exclude = fs.existsSync(excludeFile) ? fs.readFileSync(excludeFile, 'utf8') : '';
|
|
4202
|
+
if (!exclude.split(/\r?\n/).includes('/node_modules')) {
|
|
4203
|
+
fs.appendFileSync(excludeFile, `${exclude && !exclude.endsWith('\n') ? '\n' : ''}/node_modules\n`);
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
if (!checkIgnored()) throw new Error('git still tracks node_modules after updating the shared exclude file');
|
|
4207
|
+
fs.symlinkSync(sourceModules, targetModules, 'dir');
|
|
4208
|
+
nodeModulesSource = sourceModules;
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
} catch (error) {
|
|
4212
|
+
console.warn(`warning: could not link node_modules: ${String(error.message).split(/\r?\n/)[0]}`);
|
|
4213
|
+
}
|
|
4167
4214
|
} catch (e) {
|
|
4168
4215
|
exitMissionError(`[mission start] worktree creation failed: ${e.message}`, 2, asJson);
|
|
4169
4216
|
}
|
|
4170
4217
|
mission.worktree = { path: created.path, branch: created.branch, base: created.base };
|
|
4218
|
+
if (nodeModulesSource) mission.node_modules_source = nodeModulesSource;
|
|
4171
4219
|
process.chdir(created.path);
|
|
4172
4220
|
}
|
|
4173
4221
|
if (mission.xp_task_enabled) {
|
|
@@ -4225,6 +4273,8 @@ function startMission(args, options = {}) {
|
|
|
4225
4273
|
dirty_count: worktreeBaseline.dirty_count,
|
|
4226
4274
|
dirty_hash: worktreeBaseline.dirty_hash,
|
|
4227
4275
|
} : null,
|
|
4276
|
+
node_modules_linked: Boolean(saved.node_modules_source),
|
|
4277
|
+
node_modules_source: saved.node_modules_source || null,
|
|
4228
4278
|
...(saved.remap_reason ? { remap_reason: saved.remap_reason } : {}),
|
|
4229
4279
|
};
|
|
4230
4280
|
if (!options.silent) {
|
|
@@ -4236,6 +4286,7 @@ function startMission(args, options = {}) {
|
|
|
4236
4286
|
`State: ${saved.status}`,
|
|
4237
4287
|
...(saved.remap_reason ? [`Remap: ${saved.remap_reason}`] : []),
|
|
4238
4288
|
...(saved.worktree ? [`Worktree: ${saved.worktree.path}`, `Branch: ${saved.worktree.branch}`] : []),
|
|
4289
|
+
...(saved.node_modules_source ? [`linked node_modules from ${saved.node_modules_source}`] : []),
|
|
4239
4290
|
...warnings.map((warning) => `Warning: ${warning.message}`),
|
|
4240
4291
|
...(saved.xp_task ? [`AgentXP task: ${saved.xp_task.ref}`] : []),
|
|
4241
4292
|
...(saved.worktree ? [`Next: cd ${saved.worktree.path} && ${nextTickCommand}`] : [`Next: ${nextTickCommand}`]),
|
|
@@ -8049,12 +8100,6 @@ function isWithinActiveHours(activeHours, now = new Date()) {
|
|
|
8049
8100
|
return cur >= start || cur < end;
|
|
8050
8101
|
}
|
|
8051
8102
|
|
|
8052
|
-
function sleepSync(ms) {
|
|
8053
|
-
const waitMs = Math.max(0, Number(ms) || 0);
|
|
8054
|
-
if (!waitMs) return;
|
|
8055
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, waitMs);
|
|
8056
|
-
}
|
|
8057
|
-
|
|
8058
8103
|
function missionLockOwnerIsAlive(pid) {
|
|
8059
8104
|
const ownerPid = Number(pid);
|
|
8060
8105
|
if (!Number.isInteger(ownerPid) || ownerPid <= 0) return false;
|
|
@@ -11629,6 +11674,7 @@ module.exports = {
|
|
|
11629
11674
|
missionProtectedLaneHold,
|
|
11630
11675
|
missionCommand,
|
|
11631
11676
|
startMission,
|
|
11677
|
+
resolveMission,
|
|
11632
11678
|
spawnMissionDriver,
|
|
11633
11679
|
missionRunnerNeedsLiveSession,
|
|
11634
11680
|
completeMission,
|
package/commands/pack.js
CHANGED
|
@@ -1622,7 +1622,7 @@ async function publishPack(rawArgs, cwd = process.cwd(), options = {}) {
|
|
|
1622
1622
|
}
|
|
1623
1623
|
|
|
1624
1624
|
// pack.json belongs to the pack, not to whatever repo happens to contain it
|
|
1625
|
-
// (2026-07-27: publishing
|
|
1625
|
+
// (2026-07-27: publishing a backend workspace's atris folder wrote a pack.json
|
|
1626
1626
|
// into atrisos-backend/ and polluted the source repo).
|
|
1627
1627
|
const manifestPath = path.join(sourceDir, 'pack.json');
|
|
1628
1628
|
const existing = readJson(manifestPath);
|
|
@@ -2453,7 +2453,7 @@ function printCapabilityTrustCard(policy, trust, receiptPath, userDenyRuleCount
|
|
|
2453
2453
|
if (options.operatorInput) {
|
|
2454
2454
|
console.log(` operator input: ${options.operatorInput.bytes} bytes injected; source path withheld from the pack and receipt`);
|
|
2455
2455
|
}
|
|
2456
|
-
console.log(` host shell: ${policy.grantedCapabilities.includes('host.shell') ? '
|
|
2456
|
+
console.log(` host shell: ${policy.grantedCapabilities.includes('host.shell') ? 'granted, bash can reach host files and network' : 'denied'}`);
|
|
2457
2457
|
console.log(' receipt coverage: Atris hook tool events; direct slash-skill invocations and later Claude or policy denials may not appear');
|
|
2458
2458
|
console.log(` receipt: ${receiptPath}`);
|
|
2459
2459
|
}
|
package/commands/probe.js
CHANGED
|
@@ -454,18 +454,24 @@ async function probeCommand(argv) {
|
|
|
454
454
|
const detail = ok
|
|
455
455
|
? `answer: ${resultText.slice(0, 80).replace(/\n/g, ' ')}`
|
|
456
456
|
: `dead-end: ${deadEnd}`;
|
|
457
|
-
const line =
|
|
457
|
+
const line = buildProbeLine({ stamp, where, model: a.model, member, engine, ok, secs, toolsRun, calendar: a.calendar, cliCalendarOps, detail });
|
|
458
|
+
console.log(line);
|
|
459
|
+
return ok ? 0 : 1;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function buildProbeLine({ stamp, where, model, member, engine, ok, secs, toolsRun, calendar, cliCalendarOps, detail }) {
|
|
463
|
+
const line = `- **atris-probe** \`${stamp}\` - ${where} · ${model}`
|
|
458
464
|
+ (member ? ` · member=${member}` : '')
|
|
459
465
|
+ (engine ? ` · engine=${engine}` : '')
|
|
460
|
-
+ ` · ${ok ? '
|
|
461
|
-
+ (
|
|
466
|
+
+ ` · ${ok ? 'pass' : 'fail'} · ${secs}s · tools=${toolsRun}`
|
|
467
|
+
+ (calendar ? ` cal_cli=${cliCalendarOps}` : '')
|
|
462
468
|
+ ` · ${detail}`;
|
|
463
|
-
|
|
464
|
-
return ok ? 0 : 1;
|
|
469
|
+
return line.replace(/\s*\u2014\s*/g, ', ');
|
|
465
470
|
}
|
|
466
471
|
|
|
467
472
|
module.exports = {
|
|
468
473
|
probeCommand,
|
|
474
|
+
buildProbeLine,
|
|
469
475
|
runAtris2Turn,
|
|
470
476
|
// exported for tests
|
|
471
477
|
normalizePath,
|
package/commands/pull.js
CHANGED
|
@@ -722,7 +722,7 @@ async function pullBusiness(slug) {
|
|
|
722
722
|
fs.mkdirSync(path.dirname(localPath), { recursive: true });
|
|
723
723
|
fs.writeFileSync(localPath, content);
|
|
724
724
|
}
|
|
725
|
-
console.log(` \u26A0 ${p.replace(/^\//, '')}
|
|
725
|
+
console.log(` \u26A0 ${p.replace(/^\//, '')}: conflict, both you and the computer changed this`);
|
|
726
726
|
console.log(` \u2192 Remote version saved as ${p.replace(/^\//, '')}.remote`);
|
|
727
727
|
conflictCount++;
|
|
728
728
|
}
|
package/commands/push.js
CHANGED
|
@@ -985,7 +985,7 @@ async function pushAtris() {
|
|
|
985
985
|
console.log(` ... +${failedToLand.length - shown.length} more`);
|
|
986
986
|
}
|
|
987
987
|
console.log('');
|
|
988
|
-
console.log('
|
|
988
|
+
console.log(' common causes: path is outside the workspace (for example, an absolute home path),');
|
|
989
989
|
console.log(' your role lacks write permission for that folder, or warm runner returned an error.');
|
|
990
990
|
console.log(' These files will appear as drift on your next push so you can retry.');
|
|
991
991
|
}
|
package/commands/rainmaker.js
CHANGED
|
@@ -9,22 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
|
-
const os = require('os');
|
|
13
12
|
const path = require('path');
|
|
14
13
|
const { spawnSync } = require('child_process');
|
|
15
|
-
|
|
16
|
-
function resolveBackendRoot() {
|
|
17
|
-
const candidates = [
|
|
18
|
-
process.env.ATRIS_BACKEND_DIR,
|
|
19
|
-
path.resolve(__dirname, '..', '..', 'atrisos-backend'),
|
|
20
|
-
path.resolve(process.cwd(), '..', 'atrisos-backend'),
|
|
21
|
-
path.join(os.homedir(), 'arena', 'atrisos-backend'),
|
|
22
|
-
].filter(Boolean);
|
|
23
|
-
for (const root of candidates) {
|
|
24
|
-
if (fs.existsSync(path.join(root, 'scripts', 'rainmaker.py'))) return root;
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
14
|
+
const { BACKEND_ROOT_HINT, resolveBackendRoot } = require('../utils/backend-root');
|
|
28
15
|
|
|
29
16
|
function rainmakerCommand(args = []) {
|
|
30
17
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
@@ -32,8 +19,8 @@ function rainmakerCommand(args = []) {
|
|
|
32
19
|
return 0;
|
|
33
20
|
}
|
|
34
21
|
const root = resolveBackendRoot();
|
|
35
|
-
if (!root) {
|
|
36
|
-
console.error(
|
|
22
|
+
if (!root || !fs.existsSync(path.join(root, 'scripts', 'rainmaker.py'))) {
|
|
23
|
+
console.error(BACKEND_ROOT_HINT);
|
|
37
24
|
return 1;
|
|
38
25
|
}
|
|
39
26
|
const script = path.join(root, 'scripts', 'rainmaker.py');
|
package/commands/spaceship.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* atris spaceship, bounded, self-reporting overnight runner.
|
|
5
5
|
*
|
|
6
6
|
* Thin wrapper over scripts/spaceship.sh. The script is the implementation
|
|
7
|
-
* (a supervised loop that survives bad ticks and emails
|
|
7
|
+
* (a supervised loop that survives bad ticks and emails the operator on every
|
|
8
8
|
* meaningful state change); this module just makes it reachable as
|
|
9
9
|
* `atris spaceship ...` and integrates with the CLI's command dispatch.
|
|
10
10
|
*
|
package/commands/stream.js
CHANGED
|
@@ -293,6 +293,31 @@ function pushEvent(events, input) {
|
|
|
293
293
|
if (event) events.push({ ...event, key: eventKey(event) });
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
// When timestamps match, state snapshots precede receipts, then landing summaries.
|
|
297
|
+
const STREAM_SOURCE_RANK = {
|
|
298
|
+
task: 20,
|
|
299
|
+
task_episode: 30,
|
|
300
|
+
scorecard: 40,
|
|
301
|
+
mission_event: 50,
|
|
302
|
+
mission_state: 60,
|
|
303
|
+
mission_receipt: 70,
|
|
304
|
+
mission_landing: 80,
|
|
305
|
+
worktree: 85,
|
|
306
|
+
landing: 90,
|
|
307
|
+
state: 10,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
function compareStreamEvents(a, b) {
|
|
311
|
+
const byMs = a.ms - b.ms;
|
|
312
|
+
if (byMs) return byMs;
|
|
313
|
+
const rankA = STREAM_SOURCE_RANK[a.source] ?? 55;
|
|
314
|
+
const rankB = STREAM_SOURCE_RANK[b.source] ?? 55;
|
|
315
|
+
if (rankA !== rankB) return rankA - rankB;
|
|
316
|
+
const byKey = String(a.key || '').localeCompare(String(b.key || ''));
|
|
317
|
+
if (byKey) return byKey;
|
|
318
|
+
return String(a.event || '').localeCompare(String(b.event || ''));
|
|
319
|
+
}
|
|
320
|
+
|
|
296
321
|
function statePaths(root) {
|
|
297
322
|
const stateDir = path.join(root, '.atris', 'state');
|
|
298
323
|
return {
|
|
@@ -455,7 +480,6 @@ function collectRunReceiptEvents(root, deps, events) {
|
|
|
455
480
|
|
|
456
481
|
function candidateWorktreeDirs(root, deps) {
|
|
457
482
|
const dirs = new Set([
|
|
458
|
-
path.resolve(root, '..', '.agent-worktrees'),
|
|
459
483
|
path.join(path.dirname(root), '.agent-worktrees', path.basename(root)),
|
|
460
484
|
]);
|
|
461
485
|
const sidecar = readJson(statePaths(root).sidecar, deps, null);
|
|
@@ -606,7 +630,7 @@ function collectStreamEvents({ root = process.cwd(), sinceMs = 0, agent = '', no
|
|
|
606
630
|
if (wantedAgent && event.agent.toLowerCase() !== wantedAgent) continue;
|
|
607
631
|
byKey.set(event.key, event);
|
|
608
632
|
}
|
|
609
|
-
return [...byKey.values()].sort(
|
|
633
|
+
return [...byKey.values()].sort(compareStreamEvents);
|
|
610
634
|
}
|
|
611
635
|
|
|
612
636
|
function waitingOnOperator(tasks) {
|
|
@@ -841,6 +865,7 @@ function streamCommand(args = [], deps = defaultDeps()) {
|
|
|
841
865
|
module.exports = {
|
|
842
866
|
collectSnapshot,
|
|
843
867
|
collectStreamEvents,
|
|
868
|
+
compareStreamEvents,
|
|
844
869
|
createStreamState,
|
|
845
870
|
parseArgs,
|
|
846
871
|
parseSince,
|