@nac3/forge-cli 1.0.50 → 1.0.52
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/README.md +466 -390
- package/dist/chat/agent_scope.d.ts +41 -0
- package/dist/chat/agent_scope.d.ts.map +1 -0
- package/dist/chat/agent_scope.js +80 -0
- package/dist/chat/agent_scope.js.map +1 -0
- package/dist/chat/agent_session.d.ts +46 -0
- package/dist/chat/agent_session.d.ts.map +1 -0
- package/dist/chat/agent_session.js +169 -0
- package/dist/chat/agent_session.js.map +1 -0
- package/dist/chat/bridge.d.ts +8 -0
- package/dist/chat/bridge.d.ts.map +1 -1
- package/dist/chat/bridge.js +8 -3
- package/dist/chat/bridge.js.map +1 -1
- package/dist/chat/doctrine_digest.d.ts +6 -1
- package/dist/chat/doctrine_digest.d.ts.map +1 -1
- package/dist/chat/doctrine_digest.js +7 -2
- package/dist/chat/doctrine_digest.js.map +1 -1
- package/dist/chat/panel.d.ts.map +1 -1
- package/dist/chat/panel.js +326 -8
- package/dist/chat/panel.js.map +1 -1
- package/dist/chat/server.d.ts +20 -1
- package/dist/chat/server.d.ts.map +1 -1
- package/dist/chat/server.js +122 -7
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/tools/nac3_invoke.d.ts.map +1 -1
- package/dist/chat/tools/nac3_invoke.js +57 -1
- package/dist/chat/tools/nac3_invoke.js.map +1 -1
- package/dist/chat/tools/session_verbs.d.ts +39 -0
- package/dist/chat/tools/session_verbs.d.ts.map +1 -0
- package/dist/chat/tools/session_verbs.js +299 -0
- package/dist/chat/tools/session_verbs.js.map +1 -0
- package/dist/chat/tools.d.ts.map +1 -1
- package/dist/chat/tools.js +16 -0
- package/dist/chat/tools.js.map +1 -1
- package/dist/commands/chat.d.ts +18 -4
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +81 -2
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/pilot.d.ts.map +1 -1
- package/dist/commands/pilot.js +3 -1
- package/dist/commands/pilot.js.map +1 -1
- package/dist/core/file_tree.d.ts +27 -0
- package/dist/core/file_tree.d.ts.map +1 -0
- package/dist/core/file_tree.js +87 -0
- package/dist/core/file_tree.js.map +1 -0
- package/dist/core/limits_config.d.ts +10 -0
- package/dist/core/limits_config.d.ts.map +1 -1
- package/dist/core/limits_config.js +9 -0
- package/dist/core/limits_config.js.map +1 -1
- package/dist/docs/doctrine/session-orchestration.md +86 -0
- package/dist/nac3/internal_manifest.d.ts.map +1 -1
- package/dist/nac3/internal_manifest.js +103 -0
- package/dist/nac3/internal_manifest.js.map +1 -1
- package/dist/nac3/manifest_lazy.d.ts +5 -0
- package/dist/nac3/manifest_lazy.d.ts.map +1 -1
- package/dist/nac3/manifest_lazy.js +3 -3
- package/dist/nac3/manifest_lazy.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/doctrine/session-orchestration.md +86 -0
- package/package.json +1 -1
- package/src/i18n/catalogs/ar.json +13 -2
- package/src/i18n/catalogs/de.json +13 -2
- package/src/i18n/catalogs/en.json +13 -2
- package/src/i18n/catalogs/es.json +13 -2
- package/src/i18n/catalogs/fr.json +13 -2
- package/src/i18n/catalogs/hi.json +13 -2
- package/src/i18n/catalogs/it.json +13 -2
- package/src/i18n/catalogs/ja.json +13 -2
- package/src/i18n/catalogs/pt.json +13 -2
- package/src/i18n/catalogs/zh.json +13 -2
package/dist/chat/server.js
CHANGED
|
@@ -261,7 +261,9 @@ export async function startChatServer(opts) {
|
|
|
261
261
|
* ephemeral runs (vitest sets VITEST; also skip an obvious temp root). */
|
|
262
262
|
const isTestEnv = !!process.env.VITEST || process.env.NODE_ENV === 'test';
|
|
263
263
|
const isTempRoot = /[\\/](?:te?mp|yf-[a-z-]*proj|yf-startup)/i.test(opts.projectRoot);
|
|
264
|
-
|
|
264
|
+
/* A headless agent sub-session must NEVER touch the global registry --
|
|
265
|
+
* it would flip the parent's active project (Pablo's scramble bug). */
|
|
266
|
+
if (!isTestEnv && !isTempRoot && !opts.agentMode) {
|
|
265
267
|
void (async () => {
|
|
266
268
|
try {
|
|
267
269
|
await upsertProject({ slug: projectSlug, path: opts.projectRoot });
|
|
@@ -439,7 +441,12 @@ export async function startChatServer(opts) {
|
|
|
439
441
|
* so if the user pairs a phone MID-SESSION it wakes up automatically (no
|
|
440
442
|
* restart needed). When the relay is off it is a cheap no-op timer. */
|
|
441
443
|
let commandConsumerHandle = null;
|
|
444
|
+
/* A headless agent sub-session does NOT consume phone commands -- every
|
|
445
|
+
* yf chat polls the same relay stream, so a child would race the parent
|
|
446
|
+
* for ownership and could run a mobile command twice. */
|
|
442
447
|
try {
|
|
448
|
+
if (opts.agentMode)
|
|
449
|
+
throw new Error('agent-mode: relay consumer disabled');
|
|
443
450
|
commandConsumerHandle = startDesktopCommandConsumer(opts.port, {
|
|
444
451
|
/* live so a reanchor moves which commands this instance owns */
|
|
445
452
|
getActiveSlug: () => session.projectSlug,
|
|
@@ -525,6 +532,7 @@ export async function startChatServer(opts) {
|
|
|
525
532
|
url,
|
|
526
533
|
port: opts.port,
|
|
527
534
|
store,
|
|
535
|
+
bridge: bridgeHandle,
|
|
528
536
|
close: () => new Promise((resolve) => {
|
|
529
537
|
/* alpha.59w -- stop the component watcher on shutdown so
|
|
530
538
|
* vitest does not hang on lingering fs watchers. */
|
|
@@ -1934,6 +1942,18 @@ async function route(req, res, ctx) {
|
|
|
1934
1942
|
await handleProjectsAdd(req, res);
|
|
1935
1943
|
return;
|
|
1936
1944
|
}
|
|
1945
|
+
if (req.method === 'POST' && url.pathname === '/api/forge/projects/remove') {
|
|
1946
|
+
await handleProjectsRemove(req, res);
|
|
1947
|
+
return;
|
|
1948
|
+
}
|
|
1949
|
+
if (req.method === 'GET' && url.pathname === '/api/forge/files-tree') {
|
|
1950
|
+
await handleFilesTree(res, ctx);
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
if (req.method === 'POST' && url.pathname === '/api/forge/open-native') {
|
|
1954
|
+
await handleOpenNative(req, res, ctx);
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1937
1957
|
if (req.method === 'POST' && url.pathname.startsWith('/api/vault/test/')) {
|
|
1938
1958
|
const slot = url.pathname.slice('/api/vault/test/'.length);
|
|
1939
1959
|
await handleVaultTest(req, res, slot);
|
|
@@ -3831,6 +3851,15 @@ async function handleStartupTabs(_req, res, ctx, url) {
|
|
|
3831
3851
|
kind: 'tasks',
|
|
3832
3852
|
content: '',
|
|
3833
3853
|
});
|
|
3854
|
+
/* 6: File explorer -- the tree is fetched live by the panel from
|
|
3855
|
+
* /api/forge/files-tree (so the 12s startup-tabs refresh does NOT
|
|
3856
|
+
* re-scan the repo); the content is a stable placeholder. */
|
|
3857
|
+
tabs.push({
|
|
3858
|
+
key: 'startup_files',
|
|
3859
|
+
title: t('startup.tab.files'),
|
|
3860
|
+
kind: 'files',
|
|
3861
|
+
content: '{}',
|
|
3862
|
+
});
|
|
3834
3863
|
sendJson(res, 200, { ok: true, enabled: true, tabs });
|
|
3835
3864
|
}
|
|
3836
3865
|
/** Formato de id de pending para display (PND-NNN). */
|
|
@@ -4388,6 +4417,62 @@ async function handleProjectsAdd(req, res) {
|
|
|
4388
4417
|
const reg = await readRegistry();
|
|
4389
4418
|
sendJson(res, 200, { ok: true, slug, registry: reg });
|
|
4390
4419
|
}
|
|
4420
|
+
/** Remove a project from the local registry (the panel "X"). Never touches
|
|
4421
|
+
* the repo on disk -- removeProject only edits projects.json (and clears
|
|
4422
|
+
* active_slug if the removed one was active). */
|
|
4423
|
+
async function handleProjectsRemove(req, res) {
|
|
4424
|
+
let body;
|
|
4425
|
+
try {
|
|
4426
|
+
body = JSON.parse(await readBody(req));
|
|
4427
|
+
}
|
|
4428
|
+
catch {
|
|
4429
|
+
sendJson(res, 400, { ok: false, error: 'invalid JSON' });
|
|
4430
|
+
return;
|
|
4431
|
+
}
|
|
4432
|
+
if (typeof body.slug !== 'string' || body.slug.trim() === '') {
|
|
4433
|
+
sendJson(res, 400, { ok: false, error: 'slug (string) required' });
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
const { removeProject } = await import('../core/projects.js');
|
|
4437
|
+
const reg = await removeProject(body.slug);
|
|
4438
|
+
sendJson(res, 200, { ok: true, active: reg.active_slug, registry: reg });
|
|
4439
|
+
}
|
|
4440
|
+
/** Directory tree of the active project, for the file-explorer tab. */
|
|
4441
|
+
async function handleFilesTree(res, ctx) {
|
|
4442
|
+
try {
|
|
4443
|
+
const { listProjectFiles } = await import('../core/file_tree.js');
|
|
4444
|
+
const tree = await listProjectFiles(ctx.projectRoot);
|
|
4445
|
+
sendJson(res, 200, { ok: true, root_name: tree.root.name, tree: tree.root, truncated: tree.truncated, node_count: tree.node_count });
|
|
4446
|
+
}
|
|
4447
|
+
catch (err) {
|
|
4448
|
+
sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
/** Open a project file with the OS-associated application (the file-explorer
|
|
4452
|
+
* click). Delegates to desktop_open.openNative, which gates the path to the
|
|
4453
|
+
* project root and refuses executable/script extensions. */
|
|
4454
|
+
async function handleOpenNative(req, res, ctx) {
|
|
4455
|
+
let body;
|
|
4456
|
+
try {
|
|
4457
|
+
body = JSON.parse(await readBody(req));
|
|
4458
|
+
}
|
|
4459
|
+
catch {
|
|
4460
|
+
sendJson(res, 400, { ok: false, error: 'invalid JSON' });
|
|
4461
|
+
return;
|
|
4462
|
+
}
|
|
4463
|
+
if (typeof body.path !== 'string' || body.path.trim() === '') {
|
|
4464
|
+
sendJson(res, 400, { ok: false, error: 'path (string) required' });
|
|
4465
|
+
return;
|
|
4466
|
+
}
|
|
4467
|
+
try {
|
|
4468
|
+
const { openNative } = await import('../core/desktop_open.js');
|
|
4469
|
+
const result = await openNative(body.path, ctx.projectRoot);
|
|
4470
|
+
sendJson(res, result.ok ? 200 : 400, result);
|
|
4471
|
+
}
|
|
4472
|
+
catch (err) {
|
|
4473
|
+
sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4391
4476
|
async function handleTargetGet(res, ctx) {
|
|
4392
4477
|
const r = await resolveTarget(ctx.projectRoot);
|
|
4393
4478
|
sendJson(res, 200, {
|
|
@@ -5013,6 +5098,16 @@ async function handleChat(req, res, ctx) {
|
|
|
5013
5098
|
mcpAvailable = bridges.some((b) => b.enabled);
|
|
5014
5099
|
}
|
|
5015
5100
|
catch (_) { /* registry failure -> stay with MCP-off behaviour */ }
|
|
5101
|
+
/* Multi-agent session orchestration toggle (Pablo 2026-06-28). Default
|
|
5102
|
+
* OFF: the yujin.session.* verbs + their doctrine are hidden from the
|
|
5103
|
+
* prompt and the brain is told the feature is off. The user opts in via
|
|
5104
|
+
* Ajustes > Limites (multiagent.enabled). */
|
|
5105
|
+
let multiagentEnabled = false;
|
|
5106
|
+
try {
|
|
5107
|
+
const { readLimitsConfig } = await import('../core/limits_config.js');
|
|
5108
|
+
multiagentEnabled = (await readLimitsConfig()).multiagent.enabled === true;
|
|
5109
|
+
}
|
|
5110
|
+
catch (_) { /* config failure -> stay OFF (safe default) */ }
|
|
5016
5111
|
/* alpha.59z.37 NAC-3 v3.x backend tools -- prefetch the
|
|
5017
5112
|
* adopter app's nac3_backend section (task #23). Empty
|
|
5018
5113
|
* string when the manifest does not declare it, when the
|
|
@@ -5141,7 +5236,7 @@ async function handleChat(req, res, ctx) {
|
|
|
5141
5236
|
* pizarron_create_tab tool; in API/managed it is native. */
|
|
5142
5237
|
ctx.chatProvider === 'claude-code-cli'
|
|
5143
5238
|
? (ctx.panelPushToken ? 'mcp-bridge' : 'none')
|
|
5144
|
-
: 'native', projectIdentity),
|
|
5239
|
+
: 'native', projectIdentity, multiagentEnabled),
|
|
5145
5240
|
systemDynamic: recentSessionsRecap + projectContextSection + snapshotSection + graphSection + wiringSection + toolingSection + workflowSection + approvalPostureSection,
|
|
5146
5241
|
maxTokens: _maxTokensPerTurn,
|
|
5147
5242
|
/* Plan/CLI uses Claude Code's native tools (FORGE_TOOL_SPECS = just
|
|
@@ -5341,7 +5436,7 @@ async function handleChat(req, res, ctx) {
|
|
|
5341
5436
|
model: frontierSel.model,
|
|
5342
5437
|
}, globalLearnings, mcpAvailable, backendToolsSection, lastUserText, ctx.chatProvider === 'claude-code-cli'
|
|
5343
5438
|
? (ctx.panelPushToken ? 'mcp-bridge' : 'none')
|
|
5344
|
-
: 'native', projectIdentity),
|
|
5439
|
+
: 'native', projectIdentity, multiagentEnabled),
|
|
5345
5440
|
};
|
|
5346
5441
|
const frontierReply = await frontierCaller.chat(frontierRequest);
|
|
5347
5442
|
reply = frontierReply;
|
|
@@ -5800,7 +5895,12 @@ panelMode = 'native',
|
|
|
5800
5895
|
* CLI version with the project's own version (Pablo: "Es Forge v1.0.44"
|
|
5801
5896
|
* appeared while anchored to Yuemail 0.6.4). Undefined -> the block
|
|
5802
5897
|
* falls back to name + root + Forge-CLI version (back-compat). */
|
|
5803
|
-
projectIdentity
|
|
5898
|
+
projectIdentity,
|
|
5899
|
+
/** Pablo 2026-06-28 -- whether multi-agent session orchestration
|
|
5900
|
+
* (yujin.session.*) is enabled in config. When false (default) the
|
|
5901
|
+
* session verbs + their doctrine line are filtered out and the brain is
|
|
5902
|
+
* told the feature is OFF. The caller prefetches readLimitsConfig(). */
|
|
5903
|
+
multiagentEnabled = false) {
|
|
5804
5904
|
/* alpha.58 -- tier awareness header. When the request is
|
|
5805
5905
|
* served by an eco-tier model (Gemini Flash Lite, Haiku),
|
|
5806
5906
|
* the model is told its role explicitly so it can be more
|
|
@@ -5830,7 +5930,7 @@ projectIdentity) {
|
|
|
5830
5930
|
'tengo los verbos NAC-3 enchufados" is FACTUALLY WRONG -- your',
|
|
5831
5931
|
'runtime DOES have the NAC-3 manifest wired (yujin.fs.*,',
|
|
5832
5932
|
'yujin.git.*, yujin.graph.*, yujin.wiring.*, yujin.pending.*,',
|
|
5833
|
-
'yujin.doctrine.*, yujin.nac3.*, yujin.bridge.*, plus the rest in',
|
|
5933
|
+
'yujin.doctrine.*, yujin.nac3.*, yujin.bridge.*, yujin.session.*, plus the rest in',
|
|
5834
5934
|
'the manifest stub below). When in doubt about what is available, call',
|
|
5835
5935
|
'yujin.manifest.read or yujin.nac3.discover-schemas before',
|
|
5836
5936
|
'claiming "no tengo X". Falsely claiming otherwise destroys',
|
|
@@ -6066,8 +6166,23 @@ projectIdentity) {
|
|
|
6066
6166
|
' yujin.shell.exec is for ops with NO native verb equivalent:',
|
|
6067
6167
|
' npm install, docker build, pytest, custom scripts, etc.',
|
|
6068
6168
|
'',
|
|
6169
|
+
/* Multi-agent orchestration state (Pablo 2026-06-28) -- the brain must
|
|
6170
|
+
* KNOW whether yujin.session.* is available. Default OFF. */
|
|
6171
|
+
...(multiagentEnabled
|
|
6172
|
+
? [
|
|
6173
|
+
'ORQUESTACION MULTIAGENTE: ON. Podes lanzar sub-sesiones `yf chat`',
|
|
6174
|
+
'headless con yujin.session.spawn para tareas ORTOGONALES en paralelo',
|
|
6175
|
+
'(ver la doctrina session-orchestration). Siempre yujin.session.collect',
|
|
6176
|
+
'lo que lances.', '',
|
|
6177
|
+
]
|
|
6178
|
+
: [
|
|
6179
|
+
'ORQUESTACION MULTIAGENTE: OFF. Los verbos yujin.session.* NO estan',
|
|
6180
|
+
'disponibles en esta sesion (no figuran en el manifest). No intentes',
|
|
6181
|
+
'lanzar sub-sesiones. Si el usuario quiere paralelizar con sub-sesiones,',
|
|
6182
|
+
'decile que la active en Ajustes > Limites (multiagent.enabled).', '',
|
|
6183
|
+
]),
|
|
6069
6184
|
'MANIFEST:',
|
|
6070
|
-
buildManifestForPrompt({ includeMcpVerbs: mcpAvailable }).text,
|
|
6185
|
+
buildManifestForPrompt({ includeMcpVerbs: mcpAvailable, includeSessionVerbs: multiagentEnabled }).text,
|
|
6071
6186
|
/* alpha.59z.37 NAC-3 v3.x backend tools (task #23 slice 1):
|
|
6072
6187
|
* splice the pre-built app.* verbs section. Empty string ->
|
|
6073
6188
|
* nothing appears (Invariant: identical to v3.0 when no
|
|
@@ -6083,7 +6198,7 @@ projectIdentity) {
|
|
|
6083
6198
|
* code-quality guardrails, so the condensed essence of EVERY one
|
|
6084
6199
|
* is injected mandatorily in every prompt (not on-demand). Full
|
|
6085
6200
|
* text still on-demand via yujin.doctrine.discover. */
|
|
6086
|
-
...buildDoctrineDigestLines(),
|
|
6201
|
+
...buildDoctrineDigestLines({ includeSession: multiagentEnabled }),
|
|
6087
6202
|
'=================================================================',
|
|
6088
6203
|
'PIZARRON DOCTRINE (condensed; full text on-demand)',
|
|
6089
6204
|
'=================================================================',
|