@lazyingart/agintiflow 0.20.49 → 0.20.50

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.
@@ -27,6 +27,15 @@ import { hostShellOption, platformInfo, platformLabel } from "./platform.js";
27
27
  import { captureTmuxPane, listTmuxSessions, sendTmuxKeys, startTmuxSession } from "./tmux-tools.js";
28
28
  import { languageInstruction } from "./i18n.js";
29
29
  import { flushHousekeeping } from "./housekeeping.js";
30
+ import {
31
+ buildSupervisorInstruction,
32
+ createScsPlan,
33
+ reviewScsFinish,
34
+ reviewScsProgress,
35
+ reviewScsToolResult,
36
+ shouldReviewScsProgress,
37
+ shouldReviewToolResult,
38
+ } from "./scs-controller.js";
30
39
 
31
40
  const exec = promisify(execCallback);
32
41
  const BROWSER_TOOLS = new Set(["open_url", "open_workspace_file", "preview_workspace", "click", "type", "scroll", "press", "back"]);
@@ -332,6 +341,9 @@ async function createInitialState(config, sessionId) {
332
341
  config.allowParallelScouts
333
342
  ? `Parallel DeepSeek scouts may run before complex execution. Scout count: ${config.parallelScoutCount}.`
334
343
  : "Parallel scouts are disabled.",
344
+ config.scsActive
345
+ ? "Student-Committee-Supervisor mode is active. A committee/student gate will approve a phase plan, and you will execute as the supervisor under the approved phase constraints."
346
+ : "",
335
347
  `Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
336
348
  skillContext,
337
349
  engineeringGuidance,
@@ -378,6 +390,7 @@ async function createInitialState(config, sessionId) {
378
390
  : "",
379
391
  config.allowWebSearch ? "Web search tool: enabled." : "Web search tool: disabled.",
380
392
  config.allowParallelScouts ? `Parallel scouts: enabled count=${config.parallelScoutCount}.` : "Parallel scouts: disabled.",
393
+ config.scsActive ? "SCS mode: active. Wait for the approved supervisor phase instruction before treating the plan as executable." : "",
381
394
  `Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
382
395
  skillContext,
383
396
  engineeringGuidance,
@@ -1283,6 +1296,8 @@ export async function runAgent(config) {
1283
1296
  model: config.model,
1284
1297
  routingMode: config.routingMode,
1285
1298
  routeReason: config.routeReason,
1299
+ scsActive: Boolean(config.scsActive),
1300
+ scsMode: config.enableScs || "off",
1286
1301
  goal: config.goal,
1287
1302
  });
1288
1303
  await store.appendEvent("skills.selected", {
@@ -1305,17 +1320,75 @@ export async function runAgent(config) {
1305
1320
  model: config.model,
1306
1321
  routingMode: config.routingMode,
1307
1322
  routeReason: config.routeReason,
1323
+ scsActive: Boolean(config.scsActive),
1324
+ scsMode: config.enableScs || "off",
1308
1325
  });
1309
1326
 
1310
1327
  try {
1311
1328
  throwIfAborted(config);
1312
1329
  if (!state.plan) {
1313
- const plan = await createPlan(client, config, state);
1314
- state.plan = plan;
1315
- await store.savePlan(plan);
1316
- await store.appendEvent("plan.created", { plan });
1330
+ if (config.scsActive) {
1331
+ const scsPlan = await createScsPlan(client, config, state, {
1332
+ events: await store.loadEvents(),
1333
+ taskProfile: config.taskProfile,
1334
+ goal: config.goal,
1335
+ });
1336
+ state.plan = scsPlan.plan;
1337
+ state.meta.scs = scsPlan.scs;
1338
+ state.messages.push({
1339
+ role: "user",
1340
+ content: scsPlan.supervisorInstruction,
1341
+ });
1342
+ state.meta.scs.supervisorInstructionInjected = true;
1343
+ await store.saveJsonArtifact("scs-phase-001.json", scsPlan.scs).catch(() => "");
1344
+ await store.appendEvent("scs.enabled", {
1345
+ mode: config.enableScs,
1346
+ model: `${config.provider}/${config.model}`,
1347
+ });
1348
+ await store.appendEvent("scs.committee.plan_drafted", {
1349
+ phase: scsPlan.scs.phase,
1350
+ phaseGoal: scsPlan.scs.phaseGoal,
1351
+ plan: scsPlan.plan,
1352
+ acceptanceCriteria: scsPlan.scs.acceptanceCriteria,
1353
+ });
1354
+ await store.appendEvent(`scs.student.${scsPlan.scs.student.decision}`, scsPlan.scs.student);
1355
+ await store.appendEvent("scs.supervisor.phase_started", {
1356
+ phase: scsPlan.scs.phase,
1357
+ phaseGoal: scsPlan.scs.phaseGoal,
1358
+ });
1359
+ await store.savePlan(scsPlan.plan);
1360
+ await store.appendEvent("plan.created", { plan: scsPlan.plan, scs: true });
1361
+ await store.saveState(state);
1362
+ observers.event("plan.created", { plan: scsPlan.plan, scs: true });
1363
+ observers.event("scs.student.approve_plan", scsPlan.scs.student);
1364
+ emitConsole(config, `SCS: student approved phase plan (${Math.round((scsPlan.scs.student.confidence || 0) * 100)}%).`, {
1365
+ kind: "meta",
1366
+ });
1367
+ } else {
1368
+ const plan = await createPlan(client, config, state);
1369
+ state.plan = plan;
1370
+ await store.savePlan(plan);
1371
+ await store.appendEvent("plan.created", { plan });
1372
+ await store.saveState(state);
1373
+ observers.event("plan.created", { plan });
1374
+ }
1375
+ } else if (config.scsActive && !state.meta?.scs?.supervisorInstructionInjected) {
1376
+ state.meta.scs = state.meta.scs || {
1377
+ enabled: true,
1378
+ mode: config.enableScs || "on",
1379
+ active: true,
1380
+ model: `${config.provider}/${config.model}`,
1381
+ phase: 1,
1382
+ plan: state.plan,
1383
+ finishRejects: 0,
1384
+ monitorReviews: 0,
1385
+ };
1386
+ state.messages.push({
1387
+ role: "user",
1388
+ content: buildSupervisorInstruction(state.meta.scs),
1389
+ });
1390
+ state.meta.scs.supervisorInstructionInjected = true;
1317
1391
  await store.saveState(state);
1318
- observers.event("plan.created", { plan });
1319
1392
  }
1320
1393
 
1321
1394
  if (shouldRunParallelScouts(config, state)) {
@@ -1379,6 +1452,8 @@ export async function runAgent(config) {
1379
1452
  routingMode: config.routingMode,
1380
1453
  routeReason: config.routeReason,
1381
1454
  taskProfile: config.taskProfile,
1455
+ scsActive: Boolean(config.scsActive),
1456
+ scsMode: config.enableScs || "off",
1382
1457
  commandCwd: config.commandCwd,
1383
1458
  allowShellTool: config.allowShellTool,
1384
1459
  allowWrapperTools: config.allowWrapperTools,
@@ -1399,6 +1474,7 @@ export async function runAgent(config) {
1399
1474
  emitConsole(config, `Provider: ${config.provider}`, { kind: "meta" });
1400
1475
  emitConsole(config, `Model: ${config.model}`, { kind: "meta" });
1401
1476
  emitConsole(config, `Routing: ${config.routingMode} (${config.routeReason})`, { kind: "meta" });
1477
+ if (config.scsActive) emitConsole(config, `SCS: ${config.enableScs || "on"} using main-model policy`, { kind: "meta" });
1402
1478
  emitConsole(config, `Workspace: ${config.commandCwd}`, { kind: "meta" });
1403
1479
  emitConsole(config, `Sessions: ${config.sessionsDir}`, { kind: "meta" });
1404
1480
  if (config.projectSessionsDir) emitConsole(config, `Project session index: ${config.projectSessionsDir}`, { kind: "meta" });
@@ -1517,6 +1593,37 @@ export async function runAgent(config) {
1517
1593
  continue;
1518
1594
  }
1519
1595
  const fallback = assistantMessage.content?.trim() || "No tool call returned.";
1596
+ if (config.scsActive) {
1597
+ const decision = await reviewScsFinish(client, config, state, fallback, {
1598
+ events: await store.loadEvents(),
1599
+ taskProfile: config.taskProfile,
1600
+ goal: config.goal,
1601
+ });
1602
+ state.meta.scs = state.meta.scs || { enabled: true, mode: config.enableScs || "on", active: true };
1603
+ state.meta.scs.lastStudentDecision = decision;
1604
+ await store.appendEvent(`scs.student.${decision.decision}`, decision);
1605
+ observers.event(`scs.student.${decision.decision}`, {
1606
+ decision: decision.decision,
1607
+ reason: decision.reason,
1608
+ });
1609
+ if (decision.decision === "finish_rejected") {
1610
+ state.meta.scs.finishRejects = (state.meta.scs.finishRejects || 0) + 1;
1611
+ state.messages.push({
1612
+ role: "user",
1613
+ content: [
1614
+ "SCS student rejected the proposed finish.",
1615
+ `Reason: ${decision.reason || "Finish lacked enough evidence."}`,
1616
+ "Supervisor: continue with the approved phase, collect concrete evidence, or call finish with a clear blocker.",
1617
+ ].join("\n"),
1618
+ });
1619
+ state.stepsCompleted = step;
1620
+ state.updatedAt = new Date().toISOString();
1621
+ await store.saveState(state);
1622
+ emitConsole(config, `SCS: finish rejected: ${decision.reason || "needs more evidence"}`, { kind: "meta" });
1623
+ continue;
1624
+ }
1625
+ emitConsole(config, `SCS: finish approved (${Math.round((decision.confidence || 0) * 100)}%).`, { kind: "meta" });
1626
+ }
1520
1627
  appendChatEntry(state, "assistant", fallback);
1521
1628
  await store.appendEvent("session.finished", {
1522
1629
  result: fallback,
@@ -1547,6 +1654,43 @@ export async function runAgent(config) {
1547
1654
  });
1548
1655
  await applyToolLoopGuard(state, toolResult, store, observers);
1549
1656
 
1657
+ if (config.scsActive && shouldReviewToolResult(toolResult, state)) {
1658
+ const decision = await reviewScsToolResult(client, config, state, toolResult, {
1659
+ events: await store.loadEvents(),
1660
+ taskProfile: config.taskProfile,
1661
+ goal: config.goal,
1662
+ });
1663
+ state.meta.scs = state.meta.scs || { enabled: true, mode: config.enableScs || "on", active: true };
1664
+ state.meta.scs.monitorReviews = (state.meta.scs.monitorReviews || 0) + 1;
1665
+ state.meta.scs.lastStudentDecision = decision;
1666
+ await store.appendEvent(`scs.student.${decision.decision}`, {
1667
+ ...decision,
1668
+ toolName: toolResult.toolName,
1669
+ });
1670
+ observers.event(`scs.student.${decision.decision}`, {
1671
+ decision: decision.decision,
1672
+ reason: decision.reason,
1673
+ toolName: toolResult.toolName,
1674
+ });
1675
+ if (decision.decision === "rethink_plan" || decision.decision === "reject_phase") {
1676
+ state.messages.push({
1677
+ role: "user",
1678
+ content: [
1679
+ "SCS student monitor requested a rethink based on tool evidence.",
1680
+ `Decision: ${decision.decision}`,
1681
+ `Reason: ${decision.reason || "No reason provided."}`,
1682
+ decision.nextRequiredAction ? `Next required action: ${decision.nextRequiredAction}` : "",
1683
+ "Supervisor: do not repeat the same failed call. Adjust within the approved phase or finish with a concrete blocker if the phase is invalidated.",
1684
+ ]
1685
+ .filter(Boolean)
1686
+ .join("\n"),
1687
+ });
1688
+ emitConsole(config, `SCS: ${decision.decision} after ${toolResult.toolName}: ${decision.reason || "reviewed"}`, {
1689
+ kind: "meta",
1690
+ });
1691
+ }
1692
+ }
1693
+
1550
1694
  if (toolResult.toolName === "run_command") {
1551
1695
  observers.log("command.output", {
1552
1696
  command: redactSensitiveText(toolResult.args?.command || ""),
@@ -1580,6 +1724,37 @@ export async function runAgent(config) {
1580
1724
  }
1581
1725
 
1582
1726
  if (toolResult.done) {
1727
+ if (config.scsActive) {
1728
+ const decision = await reviewScsFinish(client, config, state, toolResult.result, {
1729
+ events: await store.loadEvents(),
1730
+ taskProfile: config.taskProfile,
1731
+ goal: config.goal,
1732
+ });
1733
+ state.meta.scs = state.meta.scs || { enabled: true, mode: config.enableScs || "on", active: true };
1734
+ state.meta.scs.lastStudentDecision = decision;
1735
+ await store.appendEvent(`scs.student.${decision.decision}`, decision);
1736
+ observers.event(`scs.student.${decision.decision}`, {
1737
+ decision: decision.decision,
1738
+ reason: decision.reason,
1739
+ });
1740
+ if (decision.decision === "finish_rejected") {
1741
+ state.meta.scs.finishRejects = (state.meta.scs.finishRejects || 0) + 1;
1742
+ state.messages.push({
1743
+ role: "user",
1744
+ content: [
1745
+ "SCS student rejected the proposed finish.",
1746
+ `Reason: ${decision.reason || "Finish lacked enough evidence."}`,
1747
+ "Supervisor: continue with the approved phase, collect concrete evidence, or call finish with a clear blocker.",
1748
+ ].join("\n"),
1749
+ });
1750
+ state.stepsCompleted = step;
1751
+ state.updatedAt = new Date().toISOString();
1752
+ await store.saveState(state);
1753
+ emitConsole(config, `SCS: finish rejected: ${decision.reason || "needs more evidence"}`, { kind: "meta" });
1754
+ continue;
1755
+ }
1756
+ emitConsole(config, `SCS: finish approved (${Math.round((decision.confidence || 0) * 100)}%).`, { kind: "meta" });
1757
+ }
1583
1758
  const queuedCount = await injectQueuedUserMessages(store, state, observers);
1584
1759
  if (queuedCount > 0) {
1585
1760
  state.stepsCompleted = step;
@@ -1615,6 +1790,42 @@ export async function runAgent(config) {
1615
1790
 
1616
1791
  if (continueForQueuedInput) continue;
1617
1792
 
1793
+ if (config.scsActive && shouldReviewScsProgress(step, state)) {
1794
+ const decision = await reviewScsProgress(client, config, state, {
1795
+ events: await store.loadEvents(),
1796
+ taskProfile: config.taskProfile,
1797
+ goal: config.goal,
1798
+ });
1799
+ state.meta.scs = state.meta.scs || { enabled: true, mode: config.enableScs || "on", active: true };
1800
+ state.meta.scs.monitorReviews = (state.meta.scs.monitorReviews || 0) + 1;
1801
+ state.meta.scs.lastStudentDecision = decision;
1802
+ await store.appendEvent(`scs.student.${decision.decision}`, {
1803
+ ...decision,
1804
+ step,
1805
+ trigger: "periodic",
1806
+ });
1807
+ observers.event(`scs.student.${decision.decision}`, {
1808
+ decision: decision.decision,
1809
+ reason: decision.reason,
1810
+ trigger: "periodic",
1811
+ });
1812
+ if (decision.decision === "rethink_plan" || decision.decision === "reject_phase") {
1813
+ state.messages.push({
1814
+ role: "user",
1815
+ content: [
1816
+ "SCS student requested a periodic rethink.",
1817
+ `Decision: ${decision.decision}`,
1818
+ `Reason: ${decision.reason || "No reason provided."}`,
1819
+ decision.nextRequiredAction ? `Next required action: ${decision.nextRequiredAction}` : "",
1820
+ "Supervisor: adjust the next action within the approved phase, collect stronger evidence, or finish with a concrete blocker if the phase is invalidated.",
1821
+ ]
1822
+ .filter(Boolean)
1823
+ .join("\n"),
1824
+ });
1825
+ emitConsole(config, `SCS: periodic ${decision.decision}: ${decision.reason || "reviewed"}`, { kind: "meta" });
1826
+ }
1827
+ }
1828
+
1618
1829
  await injectQueuedUserMessages(store, state, observers);
1619
1830
 
1620
1831
  state.stepsCompleted = step;
package/src/cli.js CHANGED
@@ -34,6 +34,7 @@ import { languageLabel, resolveLanguage } from "./i18n.js";
34
34
  import { maybeAutoUpdate } from "./auto-update.js";
35
35
  import { readHousekeepingSummary } from "./housekeeping.js";
36
36
  import { handleSkillMeshCommand } from "./skillmesh.js";
37
+ import { handleAapsCliCommand } from "./aaps-adapter.js";
37
38
  import fs from "node:fs/promises";
38
39
  import path from "node:path";
39
40
  import { fileURLToPath } from "node:url";
@@ -105,6 +106,7 @@ export function parseArgs(argv) {
105
106
  image: false,
106
107
  language: "",
107
108
  autoUpdate: undefined,
109
+ enableScs: "",
108
110
  };
109
111
 
110
112
  const parts = [];
@@ -148,6 +150,16 @@ export function parseArgs(argv) {
148
150
  result.autoUpdate = true;
149
151
  continue;
150
152
  }
153
+ if (arg === "--enabless" || arg === "--enable-scs" || arg === "--scs") {
154
+ const value = readOption(argv, i);
155
+ result.enableScs = value || "on";
156
+ if (value) i += 1;
157
+ continue;
158
+ }
159
+ if (arg === "--no-enabless" || arg === "--disable-scs" || arg === "--no-scs") {
160
+ result.enableScs = "off";
161
+ continue;
162
+ }
151
163
  if (arg === "--start-url") {
152
164
  result.startUrl = readOption(argv, i);
153
165
  i += 1;
@@ -383,11 +395,40 @@ export function parseArgs(argv) {
383
395
 
384
396
  function printUsage() {
385
397
  console.log(
386
- 'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti update OR aginti models OR aginti skills [query] OR aginti skillmesh [status|off|record|share|sync|serve|service] OR aginti housekeeping [--json] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [--all-sessions] [latest|<session-id>] ["prompt"] OR aginti --remove-empty-sessions OR aginti --remove-sessions OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
398
+ 'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti update OR aginti models OR aginti aaps [status|init|files|validate|compile|check|run] OR aginti skills [query] OR aginti skillmesh [status|off|record|share|sync|serve|service] OR aginti housekeeping [--json] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [--all-sessions] [latest|<session-id>] ["prompt"] OR aginti --remove-empty-sessions OR aginti --remove-sessions OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--enabless|--enabless auto|--no-enabless] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
387
399
  );
388
400
  console.log(`Languages: ${["en", "ja", "zh-Hans", "zh-Hant", "ko", "fr", "es", "ar", "vi", "de", "ru"].map((code) => `${code}=${languageLabel(code)}`).join(", ")}`);
389
401
  }
390
402
 
403
+ function stripLeadingGlobalOptions(argv = []) {
404
+ let index = 0;
405
+ const options = {
406
+ commandCwd: "",
407
+ };
408
+ while (index < argv.length) {
409
+ const arg = argv[index];
410
+ if (arg === "--no-auto-update" || arg === "--auto-update") {
411
+ index += 1;
412
+ continue;
413
+ }
414
+ if (arg === "--language" || arg === "--lang" || arg === "-L") {
415
+ index += readOption(argv, index) ? 2 : 1;
416
+ continue;
417
+ }
418
+ if (arg === "--cwd") {
419
+ const cwd = readOption(argv, index);
420
+ if (cwd) options.commandCwd = path.resolve(cwd);
421
+ index += cwd ? 2 : 1;
422
+ continue;
423
+ }
424
+ break;
425
+ }
426
+ return {
427
+ argv: argv.slice(index),
428
+ options,
429
+ };
430
+ }
431
+
391
432
  function providerLabel(provider) {
392
433
  const normalized = String(provider || "").toLowerCase();
393
434
  if (normalized === "openai") return "OpenAI";
@@ -421,6 +462,7 @@ function agentDefaults(args) {
421
462
  allowAuxiliaryTools: args.allowAuxiliaryTools ?? true,
422
463
  allowWebSearch: args.allowWebSearch ?? true,
423
464
  allowParallelScouts: args.allowParallelScouts ?? true,
465
+ enableScs: args.enableScs || process.env.AGINTI_SCS_MODE || "off",
424
466
  parallelScoutCount: args.parallelScoutCount || (Number.isFinite(envScoutCount) && envScoutCount > 0 ? envScoutCount : 3),
425
467
  sandboxMode: args.sandboxMode || envSandboxMode || "docker-workspace",
426
468
  packageInstallPolicy: args.packageInstallPolicy || envPackageInstallPolicy || "allow",
@@ -1036,6 +1078,20 @@ export async function main(argv = process.argv.slice(2)) {
1036
1078
  });
1037
1079
  if (autoUpdateResult.restarted) process.exit(autoUpdateResult.exitCode ?? 0);
1038
1080
 
1081
+ const stripped = stripLeadingGlobalOptions(argv);
1082
+ const commandArgv = stripped.argv;
1083
+ const commandCwd = stripped.options.commandCwd || process.cwd();
1084
+
1085
+ if (commandArgv[0] === "aaps") {
1086
+ try {
1087
+ await handleAapsCliCommand(commandArgv.slice(1), { cwd: commandCwd, packageDir });
1088
+ } catch (error) {
1089
+ console.error(error instanceof Error ? error.message : String(error));
1090
+ process.exit(1);
1091
+ }
1092
+ return;
1093
+ }
1094
+
1039
1095
  if (argv.includes("--remove-empty-sessions") || argv[0] === "remove-empty-sessions") {
1040
1096
  await handleRemoveSessionsCommand({ emptyOnly: true });
1041
1097
  return;
package/src/config.js CHANGED
@@ -7,6 +7,7 @@ import { loadProjectEnv, projectPaths, resolveProjectRoot } from "./project.js";
7
7
  import { normalizeTaskProfile } from "./task-profiles.js";
8
8
  import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
9
9
  import { resolveLanguage } from "./i18n.js";
10
+ import { normalizeScsMode, shouldActivateScs } from "./scs-controller.js";
10
11
 
11
12
  function parseBoolean(value, fallback) {
12
13
  if (value === undefined) return fallback;
@@ -75,7 +76,15 @@ export function resolveRuntimeConfig(args, overrides = {}) {
75
76
  auxiliaryModel: overrides.auxiliaryModel || args.auxiliaryModel || process.env.AGINTI_AUX_MODEL || "",
76
77
  });
77
78
 
78
- const defaults = getProviderDefaults(route.provider);
79
+ const scsMode = normalizeScsMode(overrides.enableScs ?? args.enableScs ?? process.env.AGINTI_SCS_MODE ?? "off");
80
+ const scsActive = shouldActivateScs(scsMode, {
81
+ goal: args.goal || "",
82
+ taskProfile,
83
+ complexityScore: route.complexityScore,
84
+ });
85
+ const activeProvider = scsActive && route.provider !== "mock" ? modelRoles.main.provider : route.provider;
86
+ const activeModel = scsActive && route.provider !== "mock" ? modelRoles.main.model : route.model;
87
+ const defaults = getProviderDefaults(activeProvider);
79
88
  const defaultMaxSteps = recommendedMaxStepsForTask({
80
89
  goal: args.goal || "",
81
90
  taskProfile,
@@ -86,6 +95,14 @@ export function resolveRuntimeConfig(args, overrides = {}) {
86
95
  const requestedSandboxMode =
87
96
  overrides.sandboxMode || args.sandboxMode || process.env.SANDBOX_MODE || (dockerRequested ? "docker-workspace" : "host");
88
97
  const sandboxMode = normalizeSandboxMode(requestedSandboxMode);
98
+ const explicitParallelScouts =
99
+ overrides.allowParallelScouts !== undefined ||
100
+ args.allowParallelScouts !== undefined ||
101
+ process.env.AGINTI_PARALLEL_SCOUTS !== undefined;
102
+ const allowParallelScouts = parseBoolean(
103
+ overrides.allowParallelScouts ?? args.allowParallelScouts ?? process.env.AGINTI_PARALLEL_SCOUTS,
104
+ true
105
+ );
89
106
 
90
107
  return {
91
108
  ...defaults,
@@ -100,6 +117,9 @@ export function resolveRuntimeConfig(args, overrides = {}) {
100
117
  language,
101
118
  routeReason: route.reason,
102
119
  routeComplexityScore: route.complexityScore,
120
+ enableScs: scsMode,
121
+ scsActive,
122
+ scsModelPolicy: scsActive ? "main" : "route",
103
123
  modelRoles,
104
124
  routeProvider: modelRoles.route.provider,
105
125
  routeModel: modelRoles.route.model,
@@ -114,10 +134,10 @@ export function resolveRuntimeConfig(args, overrides = {}) {
114
134
  auxiliaryModel: modelRoles.auxiliary.model,
115
135
  requestedProvider,
116
136
  requestedModel: overrides.model || args.model || process.env.LLM_MODEL || "",
117
- provider: route.provider,
137
+ provider: activeProvider,
118
138
  apiKey: overrides.apiKey || defaults.apiKey,
119
139
  baseURL: overrides.baseURL || defaults.baseURL,
120
- model: route.model || defaults.model,
140
+ model: activeModel || defaults.model,
121
141
  maxSteps: parseNumber(overrides.maxSteps ?? args.maxSteps ?? process.env.MAX_STEPS, defaultMaxSteps),
122
142
  headless: parseBoolean(overrides.headless ?? args.headless ?? process.env.HEADLESS, false),
123
143
  allowedDomains: Array.isArray(overrides.allowedDomains)
@@ -136,10 +156,7 @@ export function resolveRuntimeConfig(args, overrides = {}) {
136
156
  true
137
157
  ),
138
158
  allowWebSearch: parseBoolean(overrides.allowWebSearch ?? args.allowWebSearch ?? process.env.ALLOW_WEB_SEARCH, true),
139
- allowParallelScouts: parseBoolean(
140
- overrides.allowParallelScouts ?? args.allowParallelScouts ?? process.env.AGINTI_PARALLEL_SCOUTS,
141
- true
142
- ),
159
+ allowParallelScouts: scsActive && !explicitParallelScouts ? false : allowParallelScouts,
143
160
  parallelScoutCount: clampNumber(
144
161
  parseNumber(overrides.parallelScoutCount ?? args.parallelScoutCount ?? process.env.AGINTI_SCOUT_COUNT, 3),
145
162
  1,
package/src/i18n.js CHANGED
@@ -130,6 +130,7 @@ const TRANSLATIONS = {
130
130
  helpSpare: "Open spare selector, or set e.g. /spare openai/gpt-5.4 medium.",
131
131
  helpWrapper: "Configure optional external wrapper.",
132
132
  helpAuxiliary: "Manage optional auxiliary skills, including image generation.",
133
+ helpAaps: "Manage AAPS workflows through the optional @lazyingart/aaps adapter.",
133
134
  helpNew: "Start a fresh session on the next message.",
134
135
  helpResume: "Continue a saved session.",
135
136
  helpSessions: "List recent sessions in this project.",
@@ -33,6 +33,8 @@ import {
33
33
  setSkillMeshMode,
34
34
  skillMeshModeChoices,
35
35
  } from "./skillmesh.js";
36
+ import { normalizeScsMode } from "./scs-controller.js";
37
+ import { formatAapsResult, runAapsAction } from "./aaps-adapter.js";
36
38
 
37
39
  const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
38
40
  const ansi = {
@@ -73,6 +75,7 @@ const SLASH_COMMANDS = [
73
75
  "/instructions",
74
76
  "/memory",
75
77
  "/auxiliary",
78
+ "/aaps",
76
79
  "/new",
77
80
  "/resume",
78
81
  "/sessions",
@@ -85,6 +88,8 @@ const SLASH_COMMANDS = [
85
88
  "/profile",
86
89
  "/web-search",
87
90
  "/scouts",
91
+ "/enabless",
92
+ "/scs",
88
93
  "/models",
89
94
  "/venice",
90
95
  "/route",
@@ -652,6 +657,7 @@ function printHelp() {
652
657
  ` ${command("/spare [provider/model] [reasoning]", "Open spare selector, or set e.g. /spare openai/gpt-5.4 medium.", "helpSpare")}`,
653
658
  ` ${command("/wrapper [on|off|codex model reasoning]", "Configure optional external wrapper.", "helpWrapper")}`,
654
659
  ` ${command("/auxiliary [status|grsai|venice|model [provider/model]|on|off|image]", "Manage optional auxiliary skills, including image generation.", "helpAuxiliary")}`,
660
+ ` ${command("/aaps [status|init|files|validate|compile|check|run]", "Manage AAPS workflows through the optional @lazyingart/aaps adapter.", "helpAaps")}`,
655
661
  ` ${command("/new", "Start a fresh session on the next message.", "helpNew")}`,
656
662
  ` ${command("/resume <session-id>", "Continue a saved session.", "helpResume")}`,
657
663
  ` ${command("/review [focus]", "Run a bounded repo/diff review with controlled context gathering.", "helpReview")}`,
@@ -662,6 +668,7 @@ function printHelp() {
662
668
  ` ${command("/profile <name>", "Set task profile, e.g. code, website, latex, maintenance.", "helpProfile")}`,
663
669
  ` ${command("/web-search on|off", "Enable or disable the web_search tool.", "helpWebSearch")}`,
664
670
  ` ${command("/scouts on|off|<1-10>", "Enable parallel DeepSeek scouts and set scout count.", "helpScouts")}`,
671
+ ` ${command("/enabless [auto|off|status]", "Enable Student-Committee-Supervisor gated execution.", "helpEnableScs")}`,
665
672
  ` ${command("/routing <mode>", "Set routing: smart, fast, complex, manual.", "helpRouting")}`,
666
673
  ` ${command("/provider [name]", "Open provider selector, or set deepseek/openai/qwen/venice/mock.", "helpProvider")}`,
667
674
  ` ${command("/docker on", "Use docker-workspace with approved package installs.", "helpDockerOn")}`,
@@ -1602,7 +1609,7 @@ function printStatus(state) {
1602
1609
  );
1603
1610
  printSystemLine(`profile=${state.taskProfile} maxSteps=${state.maxSteps}`);
1604
1611
  printSystemLine(
1605
- `shell=${state.allowShellTool} files=${state.allowFileTools} webSearch=${state.allowWebSearch} scouts=${state.allowParallelScouts}:${state.parallelScoutCount} auxiliary=${state.allowAuxiliaryTools} sandbox=${state.sandboxMode} installs=${state.packageInstallPolicy}`
1612
+ `shell=${state.allowShellTool} files=${state.allowFileTools} webSearch=${state.allowWebSearch} scouts=${state.allowParallelScouts}:${state.parallelScoutCount} scs=${state.enableScs || "off"} auxiliary=${state.allowAuxiliaryTools} sandbox=${state.sandboxMode} installs=${state.packageInstallPolicy}`
1606
1613
  );
1607
1614
  if (state.sandboxMode !== "host") {
1608
1615
  printSystemLine(`dockerWorkspace=/workspace -> ${state.commandCwd || process.cwd()}`);
@@ -1737,6 +1744,7 @@ function createState(args = {}) {
1737
1744
  allowAuxiliaryTools: args.allowAuxiliaryTools ?? true,
1738
1745
  allowWebSearch: args.allowWebSearch ?? true,
1739
1746
  allowParallelScouts: args.allowParallelScouts ?? true,
1747
+ enableScs: normalizeScsMode(args.enableScs || process.env.AGINTI_SCS_MODE || "off"),
1740
1748
  parallelScoutCount: args.parallelScoutCount || 3,
1741
1749
  allowWrapperTools: args.allowWrapperTools ?? false,
1742
1750
  allowDestructive: args.allowDestructive ?? false,
@@ -2629,6 +2637,35 @@ async function handleCommand(line, state, packageDir) {
2629
2637
  printAgentMessage("Usage: /auxiliary [status|select|grsai|venice|model [grsai/nano-banana-2]|on|off|image]");
2630
2638
  return true;
2631
2639
  }
2640
+ if (command === "aaps") {
2641
+ const [action = "status", ...aapsArgs] = value.split(/\s+/).filter(Boolean);
2642
+ const normalizedAction = String(action || "status").toLowerCase();
2643
+ if (normalizedAction === "on" || normalizedAction === "auto") {
2644
+ state.taskProfile = "aaps";
2645
+ state.maxSteps = Math.max(state.maxSteps, 36);
2646
+ printSystemLine(`aaps=${normalizedAction} profile=aaps maxSteps=${state.maxSteps}`);
2647
+ printAgentMessage("AAPS mode enabled. Use `/aaps init`, `/aaps validate`, `/aaps compile`, or write normal requests about .aaps workflows.");
2648
+ return true;
2649
+ }
2650
+ if (normalizedAction === "off") {
2651
+ state.taskProfile = "auto";
2652
+ printSystemLine("aaps=off profile=auto");
2653
+ return true;
2654
+ }
2655
+ try {
2656
+ const result = await runAapsAction(normalizedAction, aapsArgs, {
2657
+ cwd: state.commandCwd || process.cwd(),
2658
+ packageDir,
2659
+ });
2660
+ printAgentMessage(formatAapsResult(result));
2661
+ if (result.ok === false && result.error) {
2662
+ printSystemLine(`aaps=failed ${result.error}`);
2663
+ }
2664
+ } catch (error) {
2665
+ printAgentMessage(error instanceof Error ? error.message : String(error));
2666
+ }
2667
+ return true;
2668
+ }
2632
2669
  if (command === "new") {
2633
2670
  state.sessionId = "";
2634
2671
  printAgentMessage("Next message will start a new session.");
@@ -2769,6 +2806,40 @@ async function handleCommand(line, state, packageDir) {
2769
2806
  printSystemLine(`parallelScouts=${state.allowParallelScouts ? "on" : "off"} count=${state.parallelScoutCount}`);
2770
2807
  return true;
2771
2808
  }
2809
+ if (command === "enabless" || command === "scs") {
2810
+ const rawMode = String(value || "").trim().toLowerCase();
2811
+ const mode = value ? normalizeScsMode(value) : "on";
2812
+ const knownMode =
2813
+ !rawMode ||
2814
+ ["on", "off", "auto", "smart", "status", "?", "true", "false", "yes", "no", "y", "n", "1", "0", "enable", "disable", "enabled", "disabled"].includes(
2815
+ rawMode
2816
+ );
2817
+ if (!knownMode) {
2818
+ printAgentMessage("Usage: /enabless [auto|off|status]. `/enabless` turns SCS on for the session.");
2819
+ return true;
2820
+ }
2821
+ if (value === "status" || value === "?") {
2822
+ printAgentMessage(
2823
+ [
2824
+ `SCS mode: ${state.enableScs || "off"}`,
2825
+ "Student-Committee-Supervisor gates complex work with one main model: committee drafts, student approves/monitors, supervisor executes.",
2826
+ "Use `/enabless` for on, `/enabless auto` for complex-task auto mode, or `/enabless off` for the normal fast pipeline.",
2827
+ ].join("\n")
2828
+ );
2829
+ return true;
2830
+ }
2831
+ state.enableScs = mode;
2832
+ if (mode === "on") {
2833
+ state.allowParallelScouts = false;
2834
+ }
2835
+ printSystemLine(`scs=${state.enableScs}${state.enableScs !== "off" ? ` main=${state.mainProvider || "deepseek"}/${state.mainModel || "deepseek-v4-pro"}` : ""}`);
2836
+ if (state.enableScs === "on") {
2837
+ printAgentMessage("SCS enabled. Next run uses the main model for committee planning, student gates, and supervisor execution.");
2838
+ } else if (state.enableScs === "auto") {
2839
+ printAgentMessage("SCS auto enabled. It activates for complex, risky, or long-running tasks and stays off for simple turns.");
2840
+ }
2841
+ return true;
2842
+ }
2772
2843
  if (command === "models") {
2773
2844
  printModelRoles(state);
2774
2845
  return true;
@@ -3024,6 +3095,7 @@ async function runPrompt(prompt, state, packageDir) {
3024
3095
  allowAuxiliaryTools: state.allowAuxiliaryTools,
3025
3096
  allowWebSearch: state.allowWebSearch,
3026
3097
  allowParallelScouts: state.allowParallelScouts,
3098
+ enableScs: state.enableScs,
3027
3099
  parallelScoutCount: state.parallelScoutCount,
3028
3100
  allowWrapperTools: state.allowWrapperTools,
3029
3101
  allowDestructive: state.allowDestructive,