@dotdrelle/wiki-manager 0.15.48 → 0.15.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.
- package/README.md +34 -3
- package/docker-compose.yml +7 -1
- package/package.json +2 -2
- package/src/agent/graph.js +4 -36
- package/src/agent/graph.test.js +12 -54
- package/src/cli/wiki-manager.js +28 -4
- package/src/commands/slash.js +1 -1
- package/src/core/agentEvents.js +12 -1
- package/src/core/agentEvents.test.js +21 -0
- package/src/core/buildInfo.json +2 -2
- package/src/core/dockerCompose.test.js +18 -0
- package/src/core/mcp.js +1 -1
- package/src/orchestrator/.fuse_hidden0000001c00000001 +316 -0
- package/src/orchestrator/agentRegistry.js +54 -0
- package/src/orchestrator/agentRegistry.test.js +75 -0
- package/src/runtime/approvals.js +13 -1
- package/src/runtime/approvals.test.js +44 -0
- package/src/runtime/client.js +2 -0
- package/src/runtime/delegation.js +1 -1
- package/src/runtime/runner.js +3 -3
- package/src/runtime/server.js +12 -4
- package/src/runtime/server.test.js +63 -1
- package/src/runtime/supervisor.js +18 -1
- package/src/runtime/supervisor.test.js +43 -0
- package/src/shell/LeftPane.tsx +17 -5
- package/src/shell/StartupScreen.tsx +9 -4
- package/src/shell/repl.js +1 -0
package/README.md
CHANGED
|
@@ -637,9 +637,15 @@ with `"replans": 1` in the `/run` body.
|
|
|
637
637
|
Runtime approvals are bounded to a run, plan revision and approval class.
|
|
638
638
|
Mutating orchestrated tasks **wait for approval by default**, including tasks
|
|
639
639
|
created by a skill or a directly selected capability such as ingest or
|
|
640
|
-
pipeline. Approve them by
|
|
641
|
-
|
|
642
|
-
|
|
640
|
+
pipeline. Approve them by running `/approve`, or clicking Approve in either UI:
|
|
641
|
+
the Shell right-pane banner, or the `serve` banner.
|
|
642
|
+
|
|
643
|
+
In `serve` that banner is a **fixed overlay, visible in every centre view**. It
|
|
644
|
+
used to sit inside the composer, which the layout hides in the wiki, connectors
|
|
645
|
+
and execution views — so a restore launched from `/history` waited on an
|
|
646
|
+
approval nobody could see, and the Execution view, the one meant for monitoring,
|
|
647
|
+
could not show it either. The Shell never had that gap because its plan pane is
|
|
648
|
+
always on screen. `POST /approve` also accepts an explicit run scope.
|
|
643
649
|
|
|
644
650
|
An explicitly launched skill is also approval-gated. For an orchestrated skill,
|
|
645
651
|
the scheduler blocks each uncovered mutating task; for a `direct` skill, the
|
|
@@ -907,10 +913,35 @@ The shared `docker-compose.yml` starts one workspace stack:
|
|
|
907
913
|
Use `wiki-workspace` whenever possible so Compose receives the right project
|
|
908
914
|
name, env file, ports, and volume mounts.
|
|
909
915
|
|
|
916
|
+
`PRODUCTION_ALLOWED_STEPS` gates what `production-mcp` will accept, and an
|
|
917
|
+
omission from it is **silent**: `agent_plan` simply leaves the step's task out of
|
|
918
|
+
the fragment instead of failing. `taxonomy` was missing from the shipped default
|
|
919
|
+
for several releases, so every compose-deployed ingest ran without the taxonomy
|
|
920
|
+
barrier and left the published map stale. Keep the variable in step with the
|
|
921
|
+
in-code default of `production_mcp_server.py`: a test here asserts `taxonomy` is
|
|
922
|
+
present, and one in `agent-wiki-production` compares the whole list against that
|
|
923
|
+
in-code reference. Remember that an explicit value in your `.env` overrides the
|
|
924
|
+
default entirely.
|
|
925
|
+
|
|
910
926
|
Runtime split: the host manager/runtime uses Node.js 22+ for `node:sqlite`; the
|
|
911
927
|
interactive OpenTUI shell uses Bun 1.2+; workspace Docker services run from the
|
|
912
928
|
published images and do not depend on host `node_modules`.
|
|
913
929
|
|
|
930
|
+
Two consequences worth knowing before debugging anything:
|
|
931
|
+
|
|
932
|
+
- **The runtime is not a container.** `runtime/lifecycle.js` spawns it locally,
|
|
933
|
+
detached, from the manager sources — no Compose file declares it. Changing
|
|
934
|
+
runtime or shell code therefore needs a **restart**, never an image rebuild;
|
|
935
|
+
changing `llm-wiki` or an agent needs the image rebuilt.
|
|
936
|
+
- **The runtime starts before the workspace containers.** Its first agent
|
|
937
|
+
discovery legitimately finds them absent. `agentRegistry` keeps a known
|
|
938
|
+
agent's capabilities when a probe fails — it only refreshes `lastSeenAt`, and
|
|
939
|
+
says so in the runtime log — and the periodic re-scan re-probes the MCP
|
|
940
|
+
endpoints instead of reusing a cached status. Without both, a capability the
|
|
941
|
+
agent really has stayed missing from the registry until the next successful
|
|
942
|
+
discovery, and the only symptom was a run failing much later with
|
|
943
|
+
`No agent provides capability …`.
|
|
944
|
+
|
|
914
945
|
As of 0.11.4, the host runtime store carries a minimal format guard:
|
|
915
946
|
`PRAGMA user_version = 1` in SQLite plus `.wiki/meta.json` with
|
|
916
947
|
`schemaVersion: 1`. Unknown future versions stop startup with a clear error.
|
package/docker-compose.yml
CHANGED
|
@@ -124,7 +124,13 @@ services:
|
|
|
124
124
|
- WORKSPACE_NAME=${WORKSPACE_NAME:-workspace}
|
|
125
125
|
- WIKI_WORKSPACE_PATH=/workspace
|
|
126
126
|
- WIKI_CONFIG_PATH=${WIKI_CONFIG_PATH:-}
|
|
127
|
-
|
|
127
|
+
# `taxonomy` belongs here, and its absence is SILENT: agent_plan drops the
|
|
128
|
+
# taxonomy task from an ingest fragment when the step is not allowed
|
|
129
|
+
# (production_mcp_server.py, "taxonomy" in _ALLOWED_STEPS), without an
|
|
130
|
+
# error. Every compose-deployed ingest then ran without the Lot 4 barrier
|
|
131
|
+
# and left the published map stale — the very defect that work fixed.
|
|
132
|
+
# `copy` stays out on purpose: it is the legacy step, opt-in only.
|
|
133
|
+
- PRODUCTION_ALLOWED_STEPS=${PRODUCTION_ALLOWED_STEPS:-doctor,ingest,ingest_plan,ingest_apply,taxonomy,build,export,polish,restore,pipeline}
|
|
128
134
|
- PRODUCTION_REQUIRE_CONFIRMATION=${PRODUCTION_REQUIRE_CONFIRMATION:-false}
|
|
129
135
|
# Parallelism levers — effective concurrency ≈ recommendedConcurrency.
|
|
130
136
|
# Intermediate defaults (4/8). Low profile 2/4, high profile 8/16.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotdrelle/wiki-manager",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.50",
|
|
4
4
|
"description": "Agentic shell and orchestration cockpit for llm-wiki workspaces.",
|
|
5
5
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
6
6
|
"author": "dotrelle",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"start": "bun ./bin/wiki-manager.js",
|
|
14
|
-
"test": "node --test src/core/skillInvocation.test.js src/core/skillCompiler.test.js src/runtime/skillRun.test.js src/runtime/controlDrain.test.js src/runtime/controlCancellation.test.js src/cli/runtimeStartup.test.js src/cli/wiki-manager.test.js src/agent/graph.test.js src/agent/skillRecursion.test.js src/contracts/schemas.test.js src/core/activity.test.js src/core/env.test.js src/core/agentsCompose.test.js src/core/profileServiceStatus.test.js src/core/workspaceProfile.test.js src/core/buildInfo.test.js src/core/agentEvents.test.js src/core/skillChainView.test.js src/core/runtimeLog.test.js src/activity/activityAggregator.test.js src/graph/runGraphProjector.test.js src/core/workflow.test.js src/core/planPatch.test.js src/core/agentLoop.test.js src/core/plan.test.js src/core/mcp.test.js src/core/toolLoop.test.js src/core/documentIntake.test.js src/core/dockerCompose.test.js src/core/otherWorkspacesRunning.test.js src/core/wikiSetup.test.js src/core/wikiWorkspace.test.js src/core/wikirc.test.js src/core/workspaceInherit.test.js src/core/cacert.test.js src/core/composeOverrides.test.js src/core/setEnvValue.test.js src/core/commandFailure.test.js src/core/googleGrants.test.js src/core/modelFetch.test.js src/core/startupCheck.test.js src/core/queueStore.test.js src/orchestrator/agentRegistry.test.js src/orchestrator/capabilityRegistry.test.js src/orchestrator/capabilityResolver.test.js src/orchestrator/planValidator.test.js src/orchestrator/planIntegrator.test.js src/orchestrator/taskStatuses.test.js src/orchestrator/scheduler.test.js src/orchestrator/attemptManager.test.js src/orchestrator/resultAggregator.test.js src/orchestrator/approvalPolicy.test.js src/orchestrator/dispatcher.test.js src/orchestrator/objectiveResolver.test.js src/commands/slash.test.js src/shell/repl.test.js src/shell/setupWizardModality.test.js src/shell/setupWizardPlaceholders.test.js src/shell/setupWizardSuggestions.test.js src/shell/setupWizardDiscovery.test.js src/shell/wrapText.test.js src/runtime/lifecycle.test.js src/runtime/store.test.js src/runtime/workspaceIsolation.test.js src/runtime/controlMessages.test.js src/runtime/recoveryManager.test.js src/runtime/server.test.js src/runtime/supervisor.test.js src/runtime/delegation.test.js src/runtime/runner.test.js src/runtime/runner.e2e.test.js src/runtime/skillChain.e2e.test.js src/runtime/donna-contract.test.js src/runtime/auth.test.js",
|
|
14
|
+
"test": "node --test src/core/skillInvocation.test.js src/core/skillCompiler.test.js src/runtime/skillRun.test.js src/runtime/controlDrain.test.js src/runtime/controlCancellation.test.js src/cli/runtimeStartup.test.js src/cli/wiki-manager.test.js src/agent/graph.test.js src/agent/skillRecursion.test.js src/contracts/schemas.test.js src/core/activity.test.js src/core/env.test.js src/core/agentsCompose.test.js src/core/profileServiceStatus.test.js src/core/workspaceProfile.test.js src/core/buildInfo.test.js src/core/agentEvents.test.js src/core/skillChainView.test.js src/core/runtimeLog.test.js src/activity/activityAggregator.test.js src/graph/runGraphProjector.test.js src/core/workflow.test.js src/core/planPatch.test.js src/core/agentLoop.test.js src/core/plan.test.js src/core/mcp.test.js src/core/toolLoop.test.js src/core/documentIntake.test.js src/core/dockerCompose.test.js src/core/otherWorkspacesRunning.test.js src/core/wikiSetup.test.js src/core/wikiWorkspace.test.js src/core/wikirc.test.js src/core/workspaceInherit.test.js src/core/cacert.test.js src/core/composeOverrides.test.js src/core/setEnvValue.test.js src/core/commandFailure.test.js src/core/googleGrants.test.js src/core/modelFetch.test.js src/core/startupCheck.test.js src/core/queueStore.test.js src/orchestrator/agentRegistry.test.js src/orchestrator/capabilityRegistry.test.js src/orchestrator/capabilityResolver.test.js src/orchestrator/planValidator.test.js src/orchestrator/planIntegrator.test.js src/orchestrator/taskStatuses.test.js src/orchestrator/scheduler.test.js src/orchestrator/attemptManager.test.js src/orchestrator/resultAggregator.test.js src/orchestrator/approvalPolicy.test.js src/orchestrator/dispatcher.test.js src/orchestrator/objectiveResolver.test.js src/commands/slash.test.js src/shell/repl.test.js src/shell/setupWizardModality.test.js src/shell/setupWizardPlaceholders.test.js src/shell/setupWizardSuggestions.test.js src/shell/setupWizardDiscovery.test.js src/shell/wrapText.test.js src/runtime/lifecycle.test.js src/runtime/store.test.js src/runtime/workspaceIsolation.test.js src/runtime/controlMessages.test.js src/runtime/recoveryManager.test.js src/runtime/server.test.js src/runtime/supervisor.test.js src/runtime/delegation.test.js src/runtime/runner.test.js src/runtime/runner.e2e.test.js src/runtime/skillChain.e2e.test.js src/runtime/donna-contract.test.js src/runtime/approvals.test.js src/runtime/auth.test.js",
|
|
15
15
|
"check-versions": "node scripts/check-versions.js",
|
|
16
16
|
"prepack": "node scripts/check-versions.js",
|
|
17
17
|
"prepublishOnly": "node scripts/check-versions.js",
|
package/src/agent/graph.js
CHANGED
|
@@ -26,7 +26,7 @@ import { createAgentEvent, dispatchAgentEvent } from '../core/agentEvents.js';
|
|
|
26
26
|
import { enqueueProductionJob, ensureJobQueue, formatQueue, productionLockBusy } from '../core/jobQueue.js';
|
|
27
27
|
import { loadWorkspaceProfile, updateWorkspaceProfilePreference } from '../core/profile.js';
|
|
28
28
|
import { capabilityRegistryForSession } from '../orchestrator/capabilityRegistry.js';
|
|
29
|
-
import { fetchRuntimeState,
|
|
29
|
+
import { fetchRuntimeState, postRuntimeCancel, postRuntimeControl, postRuntimeDelegate, postRuntimeKill, postRuntimeSkill } from '../runtime/client.js';
|
|
30
30
|
import { controlLanguage } from '../runtime/controlMessages.js';
|
|
31
31
|
|
|
32
32
|
const MAX_TOOL_ITERATIONS = 80;
|
|
@@ -46,7 +46,7 @@ const MAX_SPINNER_ARG_LENGTH = 96;
|
|
|
46
46
|
const INTERNAL_TOOL_SERVERS = {
|
|
47
47
|
wiki: ['plan_set', 'plan_done'],
|
|
48
48
|
shell: ['run_command', 'read_command', 'profile_update'],
|
|
49
|
-
runtime: ['kill', 'cancel', 'status', '
|
|
49
|
+
runtime: ['kill', 'cancel', 'status', 'enqueue', 'delegate', 'run_skill'],
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
const AGENT_SLASH_COMMANDS = new Set([
|
|
@@ -164,15 +164,6 @@ const RUNTIME_STATUS_TOOL = {
|
|
|
164
164
|
},
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
-
const RUNTIME_APPROVE_TOOL = {
|
|
168
|
-
type: 'function',
|
|
169
|
-
function: {
|
|
170
|
-
name: 'runtime__approve',
|
|
171
|
-
description: 'Grant the pending approval of the active runtime run (mutating tasks wait on it). Use when the user consents in ANY phrasing: "vas-y", "ok pour l\'export", "approuve", "valide". Confirm what was approved.',
|
|
172
|
-
parameters: { type: 'object', additionalProperties: false, properties: {} },
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
|
|
176
167
|
const RUNTIME_ENQUEUE_TOOL = {
|
|
177
168
|
type: 'function',
|
|
178
169
|
function: {
|
|
@@ -364,7 +355,6 @@ function toolDefinitionForCall(session, callName) {
|
|
|
364
355
|
RUNTIME_STATUS_TOOL,
|
|
365
356
|
RUNTIME_CANCEL_TOOL,
|
|
366
357
|
RUNTIME_KILL_TOOL,
|
|
367
|
-
RUNTIME_APPROVE_TOOL,
|
|
368
358
|
RUNTIME_ENQUEUE_TOOL,
|
|
369
359
|
RUNTIME_DELEGATE_TOOL,
|
|
370
360
|
RUNTIME_RUN_SKILL_TOOL,
|
|
@@ -852,28 +842,6 @@ export async function handleRuntimeControlTool(session, tool, args = {}) {
|
|
|
852
842
|
const result = await postRuntimeCancel({ url, workspace });
|
|
853
843
|
return result.cancelled ? 'Runtime run cancellation requested.' : `No active run to cancel${result.reason ? ` (${result.reason})` : ''}.`;
|
|
854
844
|
}
|
|
855
|
-
if (tool === 'approve') {
|
|
856
|
-
const state = await fetchRuntimeState({ url, workspace });
|
|
857
|
-
const pending = (Array.isArray(state?.approvals) ? state.approvals : [])
|
|
858
|
-
.filter((approval) => approval.status === 'pending_approval');
|
|
859
|
-
const runId = state?.runId
|
|
860
|
-
?? state?.runs?.find((run) => ['running', 'pending_approval'].includes(run.status))?.id
|
|
861
|
-
?? null;
|
|
862
|
-
if (!runId || pending.length === 0) return 'No pending approval found.';
|
|
863
|
-
const approvalClasses = [...new Set(pending.flatMap((approval) => {
|
|
864
|
-
const value = approval.approvalClasses ?? approval.approvalClass ?? [];
|
|
865
|
-
return Array.isArray(value) ? value : [value];
|
|
866
|
-
}).map(String).filter(Boolean))];
|
|
867
|
-
const result = await postRuntimeApprove({
|
|
868
|
-
url,
|
|
869
|
-
workspace,
|
|
870
|
-
runId,
|
|
871
|
-
scope: 'run',
|
|
872
|
-
planRevision: state?.planRevision ?? null,
|
|
873
|
-
approvalClasses: approvalClasses.length > 0 ? approvalClasses : ['default'],
|
|
874
|
-
});
|
|
875
|
-
return result?.approved ? 'Current validated plan approved.' : 'No pending approval found.';
|
|
876
|
-
}
|
|
877
845
|
if (tool === 'delegate') {
|
|
878
846
|
const objective = String(args.objective ?? '').trim();
|
|
879
847
|
if (!objective) return 'Delegation rejected: missing objective.';
|
|
@@ -1250,7 +1218,7 @@ export function buildAgentSystemPrompt(state) {
|
|
|
1250
1218
|
workspaceProfile
|
|
1251
1219
|
? `Workspace profile (.wiki/profile.md) — durable user preferences, apply these to every reply (tone, tutoiement/vouvoiement, formatting, etc.):\n${workspaceProfile}`
|
|
1252
1220
|
: null,
|
|
1253
|
-
'Runtime control: you have runtime__status, runtime__cancel, runtime__kill
|
|
1221
|
+
'Runtime control: you have runtime__status, runtime__cancel, runtime__kill and runtime__enqueue. When the user asks to stop, remove, clean or kill the current run, its jobs or the queue ("supprime le job et la queue", "arr\u00eate tout"), call runtime__kill (or runtime__cancel for a soft stop of just the run) and confirm what was stopped. When the user explicitly asks to delete, reset, abandon or replace the current plan, call runtime__kill with purge=true; never set purge=true for a simple stop. For questions about what is running or queued, call runtime__status and answer from its data. You have no approval tool: a pending approval is granted only by the user through the approval button or the /approve command. Never grant, claim or report an approval yourself; when the user asks to proceed with pending mutations, tell them to use those controls. When the user asks for a NEW action while a run is active, do not execute it: propose runtime__enqueue (run it after) or, if they insist it replaces the current work, runtime__kill then the new action.',
|
|
1254
1222
|
'When the user asks to refresh, show, or update the displayed plan or status, call runtime__status. This is a state refresh request, not a new business capability, and must never be delegated.',
|
|
1255
1223
|
'Report every runtime control outcome exactly as the tool returned it \u2014 never embellish. If runtime__kill reports 0 run(s)/0 task(s)/0 purged, say there was nothing active to stop or purge; do NOT claim a run, plan, pending approval or queue item was removed. If runtime__status returns an error or could not be read, say the runtime state could not be retrieved and do not describe a state you never obtained. Never assert that something was cleaned, cancelled, approved or purged unless that specific tool result confirms it.',
|
|
1256
1224
|
'Durable profile updates are actions in this stabilized version: delegate them instead of writing directly.',
|
|
@@ -1285,7 +1253,7 @@ export function formatLlmUnavailableMessage(reason) {
|
|
|
1285
1253
|
|
|
1286
1254
|
function toolsForClassification(classification, writeTools, session = null) {
|
|
1287
1255
|
const controlTools = session?.runtime?.url
|
|
1288
|
-
? [RUNTIME_STATUS_TOOL, RUNTIME_CANCEL_TOOL, RUNTIME_KILL_TOOL,
|
|
1256
|
+
? [RUNTIME_STATUS_TOOL, RUNTIME_CANCEL_TOOL, RUNTIME_KILL_TOOL, RUNTIME_ENQUEUE_TOOL]
|
|
1289
1257
|
: [];
|
|
1290
1258
|
// Provider discovery and validation belong to the runtime. Hiding
|
|
1291
1259
|
// delegation while the shell snapshot is temporarily empty forced Donna
|
package/src/agent/graph.test.js
CHANGED
|
@@ -782,67 +782,25 @@ test('runtime status does not manufacture a plan', async () => {
|
|
|
782
782
|
}
|
|
783
783
|
});
|
|
784
784
|
|
|
785
|
-
test('
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
if ((options.method ?? 'GET') === 'GET') {
|
|
791
|
-
return {
|
|
792
|
-
ok: true,
|
|
793
|
-
status: 200,
|
|
794
|
-
json: async () => ({
|
|
795
|
-
running: true,
|
|
796
|
-
runId: 'run-approval',
|
|
797
|
-
planRevision: 3,
|
|
798
|
-
approvals: [
|
|
799
|
-
{ status: 'pending_approval', approvalClasses: ['workspace'] },
|
|
800
|
-
{ status: 'pending_approval', approvalClasses: ['workspace'] },
|
|
801
|
-
],
|
|
802
|
-
}),
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
return { ok: true, status: 202, json: async () => ({ approved: true, runId: 'run-approval' }) };
|
|
806
|
-
};
|
|
807
|
-
let calls = 0;
|
|
785
|
+
test('Donna has no self-approval tool during an active run', async () => {
|
|
786
|
+
// Donna must never grant her own pending approval (the recurring "spontaneous
|
|
787
|
+
// approval" regression). Approval is the user's action, expressed through the
|
|
788
|
+
// banner button or /approve — never through an LLM tool call.
|
|
789
|
+
let seenTools = [];
|
|
808
790
|
const session = sessionBase({
|
|
809
791
|
runtime: { url: 'http://runtime.test' },
|
|
810
792
|
agentProjection: { status: 'running', conversation: [], activities: [] },
|
|
811
793
|
llm: {
|
|
812
|
-
async completeWithTools() {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
return {
|
|
816
|
-
content: null,
|
|
817
|
-
message: { role: 'assistant', content: null },
|
|
818
|
-
tool_calls: [{
|
|
819
|
-
id: 'approve-run',
|
|
820
|
-
type: 'function',
|
|
821
|
-
function: { name: 'runtime__approve', arguments: '{}' },
|
|
822
|
-
}],
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
return { content: 'Plan approuvé.', message: { role: 'assistant', content: 'Plan approuvé.' }, tool_calls: null };
|
|
794
|
+
async completeWithTools({ tools }) {
|
|
795
|
+
seenTools = tools.map((tool) => tool.function.name);
|
|
796
|
+
return { content: 'ok', message: { role: 'assistant', content: 'ok' }, tool_calls: null };
|
|
826
797
|
},
|
|
827
798
|
},
|
|
828
799
|
});
|
|
829
800
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
const approval = requests.find((request) => request.url.includes('/approve'));
|
|
834
|
-
assert.deepEqual(approval.body, {
|
|
835
|
-
workspace: 'docs',
|
|
836
|
-
runId: 'run-approval',
|
|
837
|
-
itemId: null,
|
|
838
|
-
approvalId: null,
|
|
839
|
-
scope: 'run',
|
|
840
|
-
planRevision: 3,
|
|
841
|
-
approvalClasses: ['workspace'],
|
|
842
|
-
});
|
|
843
|
-
} finally {
|
|
844
|
-
globalThis.fetch = originalFetch;
|
|
845
|
-
}
|
|
801
|
+
await createAgentGraph().invoke({ input: 'oui', session });
|
|
802
|
+
assert.ok(seenTools.includes('runtime__status'), 'control tools still bound during an active run');
|
|
803
|
+
assert.ok(!seenTools.includes('runtime__approve'), 'no runtime__approve tool for Donna');
|
|
846
804
|
});
|
|
847
805
|
|
|
848
806
|
test('agent graph binds the full toolset for a "remember my preference" request, not just read-only tools', async () => {
|
|
@@ -1773,7 +1731,7 @@ test('agent graph lets Donna handle ambiguous input during a run with the contro
|
|
|
1773
1731
|
assert.match(result.response, /mettre en file/);
|
|
1774
1732
|
assert.ok(seenTools.includes('runtime__enqueue'));
|
|
1775
1733
|
assert.ok(seenTools.includes('runtime__status'));
|
|
1776
|
-
assert.ok(seenTools.includes('runtime__approve'));
|
|
1734
|
+
assert.ok(!seenTools.includes('runtime__approve'), 'no self-approval tool for Donna');
|
|
1777
1735
|
assert.ok(!seenTools.includes('production__production_start_job'), 'no write MCP tools during an active run for ambiguous intents');
|
|
1778
1736
|
});
|
|
1779
1737
|
|
package/src/cli/wiki-manager.js
CHANGED
|
@@ -567,6 +567,7 @@ async function waitForRuntimeRun(session, log, { timeoutMs, pollMs = 1500, autoA
|
|
|
567
567
|
scope: 'run',
|
|
568
568
|
planRevision,
|
|
569
569
|
approvalClasses: approvalClasses.length > 0 ? approvalClasses : ['default'],
|
|
570
|
+
caller: 'headless-auto-approve',
|
|
570
571
|
});
|
|
571
572
|
const line = `runtime-wait: auto-approved run ${currentRun.id} (revision ${planRevision})${result?.approved ? '' : ' [no pending approval matched]'}`;
|
|
572
573
|
log.push(line); console.log(line);
|
|
@@ -845,6 +846,7 @@ export async function waitForRuntimeChain(session, log, {
|
|
|
845
846
|
scope: 'run',
|
|
846
847
|
planRevision,
|
|
847
848
|
approvalClasses: approvalClasses.length > 0 ? approvalClasses : ['default'],
|
|
849
|
+
caller: 'chain-wait-auto-approve',
|
|
848
850
|
});
|
|
849
851
|
const line = `chain-wait: auto-approved run ${active.runId} (revision ${planRevision})`;
|
|
850
852
|
log.push(line); console.log(line);
|
|
@@ -978,11 +980,12 @@ async function runRuntime(argv, agent) {
|
|
|
978
980
|
payload: { message: `runtime: expired ${staleControl.length} stale queued control request(s) from a previous session` },
|
|
979
981
|
}));
|
|
980
982
|
}
|
|
981
|
-
session._onRuntimeError = (err) => {
|
|
983
|
+
session._onRuntimeError = (err, runId = null) => {
|
|
982
984
|
const message = err instanceof Error ? err.message : String(err);
|
|
983
985
|
dispatchAgentEvent(session, createAgentEvent('run_error', {
|
|
984
986
|
origin: 'runtime',
|
|
985
|
-
|
|
987
|
+
...(runId ? { runId } : {}),
|
|
988
|
+
payload: { message, workspace, ...(runId ? { runId } : {}) },
|
|
986
989
|
}));
|
|
987
990
|
};
|
|
988
991
|
context.approvalManager = createApprovalManager(session, {
|
|
@@ -991,7 +994,9 @@ async function runRuntime(argv, agent) {
|
|
|
991
994
|
: undefined,
|
|
992
995
|
});
|
|
993
996
|
session._requestApproval = (request) => context.approvalManager.requestApproval(request);
|
|
994
|
-
context.supervisor = startActivitySupervisor(session
|
|
997
|
+
context.supervisor = startActivitySupervisor(session, {
|
|
998
|
+
refreshMcp: () => refreshMcpRuntimeStatus(session),
|
|
999
|
+
});
|
|
995
1000
|
contexts.set(key, context);
|
|
996
1001
|
if (workspace && workspace !== key) contexts.set(workspace, context);
|
|
997
1002
|
return context;
|
|
@@ -1422,7 +1427,26 @@ async function runRuntime(argv, agent) {
|
|
|
1422
1427
|
const provider = agents.find((item) => (item.description?.capabilities ?? [])
|
|
1423
1428
|
.some((capability) => capability.id === body.capabilityPlan.capability));
|
|
1424
1429
|
if (!provider?.serverName) {
|
|
1425
|
-
|
|
1430
|
+
/*
|
|
1431
|
+
Say what WAS seen, not only what was missing.
|
|
1432
|
+
|
|
1433
|
+
"No agent provides capability X." leaves three very different causes
|
|
1434
|
+
indistinguishable: no agent registered at all, the right agent
|
|
1435
|
+
registered but advertising a narrower set (an operation excluded by
|
|
1436
|
+
PRODUCTION_ALLOWED_STEPS drops its whole capability from
|
|
1437
|
+
agent_describe, silently), or a name mismatch. Listing the registry
|
|
1438
|
+
turns the next occurrence into its own diagnosis instead of a guess.
|
|
1439
|
+
*/
|
|
1440
|
+
const seen = agents.map((item) => {
|
|
1441
|
+
const ids = (item.description?.capabilities ?? []).map((capability) => capability.id);
|
|
1442
|
+
return `${item.serverName ?? '?'}[${ids.join(', ') || 'none'}]`;
|
|
1443
|
+
});
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
`No agent provides capability ${body.capabilityPlan.capability}. `
|
|
1446
|
+
+ (seen.length
|
|
1447
|
+
? `Registered agents: ${seen.join('; ')}.`
|
|
1448
|
+
: 'No agent is registered: none answered agent_describe.'),
|
|
1449
|
+
);
|
|
1426
1450
|
}
|
|
1427
1451
|
const fragment = parseJsonText(formatMcpToolResult(await callMcpTool(session.mcp, provider.serverName, 'agent_plan', {
|
|
1428
1452
|
capability: body.capabilityPlan.capability,
|
package/src/commands/slash.js
CHANGED
|
@@ -1402,7 +1402,7 @@ export async function handleSlashCommand(line, context) {
|
|
|
1402
1402
|
},
|
|
1403
1403
|
});
|
|
1404
1404
|
if (result?.runId) {
|
|
1405
|
-
return { output: `▶ Run de capability accepté (${String(result.runId).slice(0, 8)}) — le plan de l'agent sera intégré et dispatché en parallèle ; approbation demandée avant les mutations (
|
|
1405
|
+
return { output: `▶ Run de capability accepté (${String(result.runId).slice(0, 8)}) — le plan de l'agent sera intégré et dispatché en parallèle ; approbation demandée avant les mutations (/approve).` };
|
|
1406
1406
|
}
|
|
1407
1407
|
return { output: `Run non démarré: ${result?.explanation ?? result?.error ?? JSON.stringify(result)}` };
|
|
1408
1408
|
}
|
package/src/core/agentEvents.js
CHANGED
|
@@ -561,7 +561,18 @@ function applyEvent(state, event) {
|
|
|
561
561
|
return;
|
|
562
562
|
case 'run_error':
|
|
563
563
|
state.status = 'error';
|
|
564
|
-
|
|
564
|
+
/*
|
|
565
|
+
"Run failed:" is part of the line, not decoration.
|
|
566
|
+
|
|
567
|
+
The serve journal keeps only the entries matching a keyword list
|
|
568
|
+
(failed, error, done, approval…). A run killed by a message that uses
|
|
569
|
+
none of those words — "No agent provides capability workspace.restore."
|
|
570
|
+
— was therefore filtered out as unimportant, and the panel showed "No
|
|
571
|
+
essential run event yet" over a run that had just died with its reason
|
|
572
|
+
already in hand. What ends a run is essential by construction; the
|
|
573
|
+
prefix states that instead of hoping the wording says so.
|
|
574
|
+
*/
|
|
575
|
+
state.logs.push(`Run failed: ${String(event.payload?.message ?? 'Agent run failed.')}`);
|
|
565
576
|
// A dead run must not leave "pending" plan steps and spinning
|
|
566
577
|
// activities in the persisted projection: they reappeared as ghosts
|
|
567
578
|
// at every relaunch ("des trucs dans le plan qui n'existent pas") and
|
|
@@ -675,3 +675,24 @@ test('a stream resumed after a discard carries only the final text', () => {
|
|
|
675
675
|
assert.equal(projection.conversation[1].content, '12 pages.');
|
|
676
676
|
assert.equal(projection.conversation[1].streaming, undefined, 'le message doit être figé');
|
|
677
677
|
});
|
|
678
|
+
|
|
679
|
+
test('run_error names the failure so the essential journal cannot filter it out', () => {
|
|
680
|
+
// La liste de mots-clés du journal serve (failed, error, done, approval…)
|
|
681
|
+
// rejetait un message qui n'en contient aucun — « No agent provides
|
|
682
|
+
// capability workspace.restore. » — et le panneau affichait « No essential
|
|
683
|
+
// run event yet » au-dessus d'un run mort avec sa raison déjà connue.
|
|
684
|
+
const state = reduceAgentEvents([
|
|
685
|
+
createAgentEvent('run_error', {
|
|
686
|
+
origin: 'runtime',
|
|
687
|
+
runId: 'run-1',
|
|
688
|
+
payload: { message: 'No agent provides capability workspace.restore.' },
|
|
689
|
+
}),
|
|
690
|
+
]);
|
|
691
|
+
|
|
692
|
+
assert.equal(state.status, 'error');
|
|
693
|
+
const line = state.logs.at(-1);
|
|
694
|
+
assert.match(line, /^Run failed: /);
|
|
695
|
+
assert.match(line, /workspace\.restore/);
|
|
696
|
+
// Le mot qui rend l'entrée « essentielle » pour le journal serve.
|
|
697
|
+
assert.match(line, /failed/i);
|
|
698
|
+
});
|
package/src/core/buildInfo.json
CHANGED
|
@@ -27,6 +27,24 @@ test('workspace production agent enables restore by default', async () => {
|
|
|
27
27
|
assert.match(String(allowed), /(?:^|,)restore(?:,|})/);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
test('every shipped default allows the taxonomy step', async () => {
|
|
31
|
+
/*
|
|
32
|
+
L'absence de `taxonomy` est SILENCIEUSE.
|
|
33
|
+
|
|
34
|
+
`agent_plan` retire la tâche taxonomique du fragment d'ingestion quand
|
|
35
|
+
l'étape n'est pas autorisée (`"taxonomy" in _ALLOWED_STEPS`), sans erreur ni
|
|
36
|
+
avertissement. Un déploiement par compose ingérait donc sans jamais publier
|
|
37
|
+
de taxonomie, et la carte restait périmée — exactement le défaut que le Lot 4
|
|
38
|
+
avait corrigé côté moteur. Le défaut de Python porte `taxonomy` ; un compose
|
|
39
|
+
POSITIONNE toujours la variable, donc ce défaut ne s'applique jamais là.
|
|
40
|
+
*/
|
|
41
|
+
const raw = await readFile(new URL('../../docker-compose.yml', import.meta.url), 'utf8');
|
|
42
|
+
const compose = YAML.parse(raw);
|
|
43
|
+
const allowed = compose.services['production-mcp'].environment
|
|
44
|
+
.find((entry) => String(entry).startsWith('PRODUCTION_ALLOWED_STEPS='));
|
|
45
|
+
assert.match(String(allowed), /(?:^|,)taxonomy(?:,|})/);
|
|
46
|
+
});
|
|
47
|
+
|
|
30
48
|
test('shipped compose files never carry a build context', async () => {
|
|
31
49
|
// Ces deux fichiers partent dans le paquet npm, où les dépôts frères
|
|
32
50
|
// (`../agent-external/…`) n'existent pas : un `build:` y rend toute commande
|
package/src/core/mcp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { managerEnvFile, managerMcpEndpointsFile, readEnvFile } from './env.js';
|
|
3
3
|
|
|
4
|
-
const WIKI_MANAGER_VERSION = '0.15.
|
|
4
|
+
const WIKI_MANAGER_VERSION = '0.15.50';
|
|
5
5
|
|
|
6
6
|
function envValue(key) {
|
|
7
7
|
const filePath = managerEnvFile();
|