@dotdrelle/wiki-manager 0.15.94 → 0.15.97
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 +39 -28
- package/package.json +2 -2
- package/src/agent/graph.js +61 -21
- package/src/agent/graph.test.js +71 -1
- package/src/cli/wiki-manager.js +35 -17
- package/src/core/agentEvents.js +21 -4
- package/src/core/agentEvents.test.js +34 -0
- package/src/core/buildInfo.json +2 -2
- package/src/core/googleGrants.js +0 -3
- package/src/core/json.js +9 -0
- package/src/core/mcp.js +1 -1
- package/src/core/plan.js +0 -4
- package/src/core/progressNotes.js +0 -4
- package/src/core/skillChainView.js +3 -1
- package/src/core/toolLoop.js +56 -2
- package/src/core/toolLoop.test.js +35 -4
- package/src/orchestrator/agentRegistry.js +1 -3
- package/src/orchestrator/dependencyResolver.js +0 -3
- package/src/orchestrator/planValidator.js +1 -3
- package/src/orchestrator/providers/runtimeProvider.js +0 -14
- package/src/orchestrator/taskStatuses.js +8 -0
- package/src/runtime/client.js +0 -16
- package/src/runtime/controlDrain.js +6 -3
- package/src/runtime/deltaCoalescer.js +53 -0
- package/src/runtime/deltaCoalescer.test.js +56 -0
- package/src/runtime/loginPage.js +0 -3
- package/src/runtime/loginSession.js +1 -4
- package/src/runtime/runner.js +17 -3
- package/src/runtime/runner.test.js +32 -1
- package/src/runtime/server.js +172 -14
- package/src/runtime/server.test.js +206 -0
- package/src/runtime/skillRun.js +2 -2
- package/src/runtime/skillRun.test.js +7 -2
- package/src/shell/repl.js +7 -3
- package/src/orchestrator/.fuse_hidden0000001c00000001 +0 -316
package/README.md
CHANGED
|
@@ -121,47 +121,60 @@ in isolated workspaces.
|
|
|
121
121
|
|
|
122
122
|
## How wikiLLM compares
|
|
123
123
|
|
|
124
|
-
Several open projects now build
|
|
124
|
+
Several open projects now build an LLM-maintained knowledge layer over your
|
|
125
|
+
sources — some as a wiki, some as a retrieval and answer platform. They target
|
|
125
126
|
**different problems** — the useful questions are *what goes in, what comes out,
|
|
126
|
-
and who operates it*. Snapshot as of 2026
|
|
127
|
+
and who operates it*. Snapshot as of **September 2026**; all of these move quickly.
|
|
127
128
|
|
|
128
129
|
✅ first-class · 🟡 partial or indirect · ❌ not a goal
|
|
129
130
|
|
|
130
|
-
Projects compared: [OpenWiki](https://github.com/langchain-ai/openwiki)
|
|
131
|
-
[DeepWiki-Open](https://github.com/asyncfuncai/deepwiki-open)
|
|
131
|
+
Projects compared: [OpenWiki](https://github.com/langchain-ai/openwiki) and
|
|
132
|
+
[DeepWiki-Open](https://github.com/asyncfuncai/deepwiki-open) (code / knowledge
|
|
133
|
+
wikis), [RAGFlow](https://github.com/infiniflow/ragflow) and
|
|
134
|
+
[Onyx](https://github.com/onyx-dot-app/onyx) (RAG platforms), and
|
|
132
135
|
[GraphRAG](https://github.com/microsoft/graphrag).
|
|
133
136
|
|
|
134
137
|
<table>
|
|
135
138
|
<thead>
|
|
136
|
-
<tr
|
|
139
|
+
<tr>
|
|
140
|
+
<th><small>Need</small></th>
|
|
141
|
+
<th><small><strong>wikiLLM</strong></small></th>
|
|
142
|
+
<th><small><strong>Code / knowledge wikis</strong><br>OpenWiki · DeepWiki-Open</small></th>
|
|
143
|
+
<th><small><strong>RAG platforms</strong><br>RAGFlow · Onyx</small></th>
|
|
144
|
+
<th><small><strong>Graph pipeline</strong><br>GraphRAG</small></th>
|
|
145
|
+
</tr>
|
|
137
146
|
</thead>
|
|
138
147
|
<tbody>
|
|
139
|
-
<tr><td><small>Input</small></td><td><small>✅ Business docs — Confluence, PDF, Office, SaaS</small></td><td><small>✅
|
|
140
|
-
<tr><td><small>Output</small></td><td><small>✅ Maintained wiki <strong>+ deliverables</strong
|
|
141
|
-
<tr><td><small>
|
|
142
|
-
<tr><td><small>
|
|
143
|
-
<tr><td><small>
|
|
144
|
-
<tr><td><small>
|
|
145
|
-
<tr><td><small>
|
|
146
|
-
<tr><td><small>
|
|
147
|
-
<tr><td><small>Connectors as services</small></td><td><small>✅ Independent MCP agents (Confluence, docs, e-mail…)</small></td><td><small>🟡 Built-in connector set</small></td><td><small>❌</small></td><td><small>❌</small></td></tr>
|
|
148
|
-
<tr><td><small>Offline / local models</small></td><td><small>✅ Per-workspace OpenAI-compatible or gateway (Ollama, vLLM, MLX…)</small></td><td><small>✅ 13+ providers</small></td><td><small>✅ Ollama</small></td><td><small>✅ Any OpenAI-compatible</small></td></tr>
|
|
149
|
-
<tr><td><small>License</small></td><td><small>❌ PolyForm <strong>Noncommercial</strong></small></td><td><small>✅ MIT</small></td><td><small>✅ MIT</small></td><td><small>✅ MIT</small></td></tr>
|
|
148
|
+
<tr><td><small>Input</small></td><td><small>✅ Business docs — Confluence, PDF, Office, SaaS</small></td><td><small>✅ Code repo; OpenWiki also personal sources (Notion, Gmail, Slack…)</small></td><td><small>✅ PDF/Office/images, Confluence, S3, 50+ connectors</small></td><td><small>🟡 Plain text</small></td></tr>
|
|
149
|
+
<tr><td><small>Output</small></td><td><small>✅ Maintained wiki <strong>+ deliverables</strong></small></td><td><small>✅ Linked Markdown wiki (+ graph / diagrams)</small></td><td><small>🟡 Grounded RAG answers, not a wiki</small></td><td><small>❌ Entity graph + summaries</small></td></tr>
|
|
150
|
+
<tr><td><small>Agents & actions</small></td><td><small>✅ Capability dispatcher, external agentic runtime, MCP tools</small></td><td><small>✅ Doc-generation agents, MCP connectors</small></td><td><small>✅ Agentic workflow, MCP actions, deep research</small></td><td><small>❌ Indexing pipeline</small></td></tr>
|
|
151
|
+
<tr><td><small>Keep current</small></td><td><small>✅ Re-ingest / scheduled</small></td><td><small>✅ <code>--update</code> / regenerate, CI</small></td><td><small>✅ Scheduled connector sync</small></td><td><small>🟡 Maintenance mode</small></td></tr>
|
|
152
|
+
<tr><td><small>Team UI</small></td><td><small>✅ Web console — wiki, graph, chat, runs</small></td><td><small>🟡 CLI + visualizer; DeepWiki-Open web app</small></td><td><small>✅ Self-hosted web app</small></td><td><small>❌ Library / CLI</small></td></tr>
|
|
153
|
+
<tr><td><small>Multi-project isolation</small></td><td><small>✅ Workspaces, own services, ports, secrets</small></td><td><small>❌ One wiki per repo / directory</small></td><td><small>🟡 Datasets / teams, not isolated services</small></td><td><small>❌ One index per corpus</small></td></tr>
|
|
154
|
+
<tr><td><small>Human approval & governance</small></td><td><small>✅ Approval-gated dispatcher, budgets, crash recovery</small></td><td><small>❌ PR review only</small></td><td><small>🟡 RBAC / SSO (Onyx enterprise)</small></td><td><small>❌</small></td></tr>
|
|
155
|
+
<tr><td><small>License</small></td><td><small>❌ PolyForm <strong>Noncommercial</strong></small></td><td><small>✅ MIT</small></td><td><small>✅ Apache-2.0 / MIT CE</small></td><td><small>✅ MIT</small></td></tr>
|
|
150
156
|
</tbody>
|
|
151
157
|
</table>
|
|
152
158
|
|
|
159
|
+
<small>Per-project details: OpenWiki emits OKF v0.2 and maintains a versioned
|
|
160
|
+
evidence graph of “grounded claims”; GraphRAG is a research project now in
|
|
161
|
+
maintenance mode; Onyx’s SSO/RBAC live in its enterprise edition.</small>
|
|
162
|
+
|
|
153
163
|
**The short version:**
|
|
154
164
|
|
|
155
|
-
- **OpenWiki** and **DeepWiki-Open**
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
- **OpenWiki** and **DeepWiki-Open** build wikis from a **code repository**
|
|
166
|
+
(OpenWiki also ingests personal sources — Notion, Gmail, Slack…). Their input
|
|
167
|
+
is code or personal feeds, not a stack of business documents feeding your
|
|
168
|
+
deliverable templates.
|
|
169
|
+
- **RAGFlow** and **Onyx** are RAG platforms: they index sources and answer
|
|
170
|
+
questions from them, with agents and connectors, but the output is an answer,
|
|
171
|
+
not a wiki you browse or a document you ship.
|
|
172
|
+
- **GraphRAG** builds retrieval structure and is now in maintenance mode — a
|
|
173
|
+
complementary back end, not a wiki.
|
|
174
|
+
- **wikiLLM** is the only one whose output is *both* a browsable wiki *and*
|
|
175
|
+
regenerated business documents, and the only one with the operational layer —
|
|
176
|
+
isolated projects, bounded approvals, automatic recovery, a web console —
|
|
177
|
+
that a shared internal tool needs.
|
|
165
178
|
|
|
166
179
|
**What wikiLLM does *not* try to do (today):**
|
|
167
180
|
|
|
@@ -170,8 +183,6 @@ Projects compared: [OpenWiki](https://github.com/langchain-ai/openwiki),
|
|
|
170
183
|
- Serve a true multi-user instance with per-user identity and an attributed
|
|
171
184
|
audit trail. This is a single-user deployment baseline (see the scope note
|
|
172
185
|
above).
|
|
173
|
-
- Expose a graph-query API over the corpus the way GraphRAG does; retrieval is
|
|
174
|
-
BM25 plus a vector index feeding generation.
|
|
175
186
|
- Ship or host the multi-provider AI gateway — routing to several providers is
|
|
176
187
|
supported, the gateway itself is infrastructure you bring.
|
|
177
188
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotdrelle/wiki-manager",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.97",
|
|
4
4
|
"description": "Agentic shell and orchestration cockpit for llm-wiki workspaces.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"start": "bun ./bin/wiki-manager.js",
|
|
22
|
-
"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/core/runtimeEventAdapter.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/wikiWorkspaceStart.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/currentArtifact.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/orchestrator/providers/fakeRuntimeProvider.test.js src/orchestrator/providers/runtimeProviders.test.js src/orchestrator/providers/dispatcherExternalRuntime.test.js src/orchestrator/providers/deepAgentsProvider.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 src/runtime/totp.test.js src/runtime/loginSession.test.js src/runtime/loginRoutes.test.js",
|
|
22
|
+
"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/core/runtimeEventAdapter.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/wikiWorkspaceStart.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/currentArtifact.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/orchestrator/providers/fakeRuntimeProvider.test.js src/orchestrator/providers/runtimeProviders.test.js src/orchestrator/providers/dispatcherExternalRuntime.test.js src/orchestrator/providers/deepAgentsProvider.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/deltaCoalescer.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 src/runtime/totp.test.js src/runtime/loginSession.test.js src/runtime/loginRoutes.test.js",
|
|
23
23
|
"check-versions": "node scripts/check-versions.js",
|
|
24
24
|
"prepack": "node scripts/check-versions.js",
|
|
25
25
|
"prepublishOnly": "node scripts/check-versions.js",
|
package/src/agent/graph.js
CHANGED
|
@@ -31,6 +31,7 @@ import { artifactFromToolCall, currentArtifactFor, currentArtifactPromptLine, re
|
|
|
31
31
|
import { capabilityRegistryForSession } from '../orchestrator/capabilityRegistry.js';
|
|
32
32
|
import { objectiveForResolution } from '../orchestrator/objectiveResolver.js';
|
|
33
33
|
import { fetchRuntimeState, postRuntimeCancel, postRuntimeControl, postRuntimeDelegate, postRuntimeKill, postRuntimeSkill } from '../runtime/client.js';
|
|
34
|
+
import { formatPublicSkillInvocation, generateSkillAcknowledgment } from '../runtime/skillRun.js';
|
|
34
35
|
|
|
35
36
|
const MAX_TOOL_ITERATIONS = 80;
|
|
36
37
|
/**
|
|
@@ -435,6 +436,19 @@ export function bareToolCallJson(content, tools = []) {
|
|
|
435
436
|
return hasArguments ? name : null;
|
|
436
437
|
}
|
|
437
438
|
|
|
439
|
+
/**
|
|
440
|
+
* A model that has NO tool to call sometimes writes the call as text:
|
|
441
|
+
* `runtime__delegate{"objective":"…"}`. Unlike `bareToolCallJson` it is not
|
|
442
|
+
* JSON and cannot be validated against the offered set (there is none), but the
|
|
443
|
+
* `<namespace>__<tool>{` shape is never legitimate prose. Observed after a
|
|
444
|
+
* terminal failure stripped the tools from the synthesis turn: the raw call
|
|
445
|
+
* reached the user and nothing ran.
|
|
446
|
+
*/
|
|
447
|
+
export function narratedToolCallText(content) {
|
|
448
|
+
const match = String(content ?? '').trim().match(/^([a-z][a-z0-9_-]*__[a-z][a-z0-9_-]*)\s*\{/i);
|
|
449
|
+
return match ? match[1] : null;
|
|
450
|
+
}
|
|
451
|
+
|
|
438
452
|
function parseActionJson(text) {
|
|
439
453
|
const cleaned = String(text ?? '').trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
|
|
440
454
|
if (!cleaned) return null;
|
|
@@ -978,7 +992,7 @@ export async function handleRuntimeControlTool(session, tool, args = {}) {
|
|
|
978
992
|
}
|
|
979
993
|
if (tool === 'run_skill') {
|
|
980
994
|
const skillName = String(args.skillName ?? '').trim();
|
|
981
|
-
if (!skillName) return JSON.stringify({ ok: false, terminal:
|
|
995
|
+
if (!skillName) return JSON.stringify({ ok: false, terminal: false, code: 'skill_not_found', message: 'skillName is required: pass the exact skill name, or delegate the objective with runtime__delegate.', availableSkills: [] });
|
|
982
996
|
const selectedSkill = findSkill(session, skillName);
|
|
983
997
|
const suppliedArguments = args.arguments && typeof args.arguments === 'object' && !Array.isArray(args.arguments)
|
|
984
998
|
? args.arguments
|
|
@@ -1377,24 +1391,6 @@ export function buildAgentSystemPrompt(state) {
|
|
|
1377
1391
|
return customPrompt ? `${customPrompt}\n\n${agentContext}` : agentContext;
|
|
1378
1392
|
}
|
|
1379
1393
|
|
|
1380
|
-
export function buildLimitedAgentResponse(state, reason = 'no workspace loaded with .wikirc.yaml') {
|
|
1381
|
-
const workspace = state.session.workspace ?? 'no workspace selected';
|
|
1382
|
-
const wikirc = state.session.wikirc?.profile ?? 'no profile loaded';
|
|
1383
|
-
return [
|
|
1384
|
-
`Donna is active. Current workspace: ${workspace}.`,
|
|
1385
|
-
`Current wikirc profile: ${wikirc}.`,
|
|
1386
|
-
'',
|
|
1387
|
-
'I am the shell agent mode: use `/agent` to route free text through this LangGraph graph, or `/chat` for direct chat.',
|
|
1388
|
-
`LLM connection: limited mode (${reason}).`,
|
|
1389
|
-
`Available primitives: ${commandList(state.session)}.`,
|
|
1390
|
-
'',
|
|
1391
|
-
'Connected MCP tools:',
|
|
1392
|
-
formatMcpToolsForAgent(state.session.mcp),
|
|
1393
|
-
'',
|
|
1394
|
-
'Limited mode: workspace, Docker Compose tools, MCP calls, /wiki fallback, skill discovery, and headless mode are wired.',
|
|
1395
|
-
'Use `/help` to see deterministic shell commands.',
|
|
1396
|
-
].join('\n');
|
|
1397
|
-
}
|
|
1398
1394
|
|
|
1399
1395
|
export function formatLlmUnavailableMessage(reason) {
|
|
1400
1396
|
const clean = String(reason ?? 'unknown reason').replace(/\s+/g, ' ').trim();
|
|
@@ -1713,10 +1709,14 @@ export function createAgentGraph(options = {}) {
|
|
|
1713
1709
|
// text really is a call to one of them: a legitimate answer that happens
|
|
1714
1710
|
// to contain JSON (a config excerpt, an API sample) must go through
|
|
1715
1711
|
// untouched, which is why this is not a "content starts with {" test.
|
|
1716
|
-
const bareCall = tools.length > 0 ? bareToolCallJson(result.content, tools) : null
|
|
1712
|
+
const bareCall = (tools.length > 0 ? bareToolCallJson(result.content, tools) : null)
|
|
1713
|
+
?? narratedToolCallText(result.content);
|
|
1717
1714
|
if (bareCall) {
|
|
1718
1715
|
const retries = Number(state.invalidToolCallRetries ?? 0);
|
|
1719
|
-
|
|
1716
|
+
// Retry only when a tool can still be called; when none are offered (the
|
|
1717
|
+
// synthesis turn after a terminal failure) the call is unexecutable and
|
|
1718
|
+
// the honest failure below is the whole answer.
|
|
1719
|
+
if (tools.length > 0 && retries < 2) {
|
|
1720
1720
|
state.session._onStreamReset?.();
|
|
1721
1721
|
state.session._onStep?.('Agent: tool call written as JSON text rejected; retrying…');
|
|
1722
1722
|
return {
|
|
@@ -1903,6 +1903,7 @@ export function createAgentGraph(options = {}) {
|
|
|
1903
1903
|
const toolCalls = state.pendingToolCalls ?? [];
|
|
1904
1904
|
const toolResultMessages = [];
|
|
1905
1905
|
let terminalFailure = null;
|
|
1906
|
+
let skillLaunch = null;
|
|
1906
1907
|
|
|
1907
1908
|
for (const call of toolCalls) {
|
|
1908
1909
|
const resolved = resolveToolCallName(state.session.mcp, call.function.name, INTERNAL_TOOL_SERVERS);
|
|
@@ -2017,6 +2018,24 @@ export function createAgentGraph(options = {}) {
|
|
|
2017
2018
|
if (skillResult?.terminal === true) {
|
|
2018
2019
|
terminalFailure = skillResult.code ?? 'skill_failed';
|
|
2019
2020
|
ok = false;
|
|
2021
|
+
} else if (skillResult?.accepted === true) {
|
|
2022
|
+
// The skill owns execution from here. Without this the turn kept
|
|
2023
|
+
// going and the model could re-delegate or refuse the very
|
|
2024
|
+
// objective it had just launched — the "launched, then no agent
|
|
2025
|
+
// capable" contradiction. End the turn with the launch
|
|
2026
|
+
// acknowledgement instead of another tool round.
|
|
2027
|
+
skillLaunch = {
|
|
2028
|
+
publicInput: formatPublicSkillInvocation(
|
|
2029
|
+
skillResult.skill ?? args.skillName ?? '',
|
|
2030
|
+
args.arguments && typeof args.arguments === 'object' ? args.arguments : {},
|
|
2031
|
+
),
|
|
2032
|
+
objectives: Number(skillResult.objectiveCount ?? skillResult.objectives ?? 1) || 1,
|
|
2033
|
+
};
|
|
2034
|
+
} else if (skillResult?.ok === false) {
|
|
2035
|
+
// A recoverable refusal (guessed skill, missing input): keep the
|
|
2036
|
+
// turn alive so the model can correct itself or delegate, but do
|
|
2037
|
+
// not let the progress note call it a success.
|
|
2038
|
+
ok = false;
|
|
2020
2039
|
}
|
|
2021
2040
|
}
|
|
2022
2041
|
if (tool === 'delegate' && /^Runtime control error \(delegate\):/i.test(resultText)) {
|
|
@@ -2181,6 +2200,24 @@ export function createAgentGraph(options = {}) {
|
|
|
2181
2200
|
invalidResponseRetries: 0,
|
|
2182
2201
|
};
|
|
2183
2202
|
}
|
|
2203
|
+
if (skillLaunch) {
|
|
2204
|
+
// Deterministic, localized acknowledgement, generated in the session
|
|
2205
|
+
// language like the `/turn` skill path. No further model turn: the skill
|
|
2206
|
+
// is launched and owns execution, so nothing can contradict it.
|
|
2207
|
+
const response = await generateSkillAcknowledgment(state.session, skillLaunch).catch(
|
|
2208
|
+
() => `Started ${skillLaunch.publicInput} — ${skillLaunch.objectives} step(s) in progress.`,
|
|
2209
|
+
);
|
|
2210
|
+
return {
|
|
2211
|
+
messages: toolResultMessages,
|
|
2212
|
+
pendingToolCalls: null,
|
|
2213
|
+
response,
|
|
2214
|
+
forceDelegation: false,
|
|
2215
|
+
retryWithoutTool: false,
|
|
2216
|
+
terminalToolFailure: false,
|
|
2217
|
+
invalidToolCallRetries: 0,
|
|
2218
|
+
invalidResponseRetries: 0,
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2184
2221
|
return {
|
|
2185
2222
|
messages: toolResultMessages,
|
|
2186
2223
|
pendingToolCalls: null,
|
|
@@ -2193,6 +2230,9 @@ export function createAgentGraph(options = {}) {
|
|
|
2193
2230
|
}
|
|
2194
2231
|
|
|
2195
2232
|
function routeToolExecutor(state) {
|
|
2233
|
+
// A skill launch already produced its final acknowledgement: end the turn
|
|
2234
|
+
// instead of routing back for another model call that could contradict it.
|
|
2235
|
+
if (state.response != null) return END;
|
|
2196
2236
|
return 'orchestrator';
|
|
2197
2237
|
}
|
|
2198
2238
|
|
package/src/agent/graph.test.js
CHANGED
|
@@ -624,7 +624,10 @@ test('an explicitly selected skill runs through the intra-runtime path with name
|
|
|
624
624
|
},
|
|
625
625
|
});
|
|
626
626
|
const result = await createAgentGraph().invoke({ input: 'lance le skill deliver avec le template Quarterly report', session });
|
|
627
|
-
|
|
627
|
+
// Launching a skill ends the turn: the acknowledgement is generated once and
|
|
628
|
+
// the model is not given a second chance to re-delegate or contradict it.
|
|
629
|
+
assert.equal(result.response, 'Started /deliver deliverable="Quarterly report" — 1 step(s) queued.');
|
|
630
|
+
assert.equal(mainCalls, 1);
|
|
628
631
|
// `skillStack` accompagne désormais la demande : le run imbriqué démarre après
|
|
629
632
|
// le nettoyage de celui-ci, et c'est le seul canal par lequel il peut savoir
|
|
630
633
|
// quelles compétences sont déjà ouvertes au-dessus de lui.
|
|
@@ -706,6 +709,73 @@ test('a terminal skill refusal stops the whole turn before a delegate fallback',
|
|
|
706
709
|
assert.equal(delegated, false);
|
|
707
710
|
});
|
|
708
711
|
|
|
712
|
+
test('a narrated tool call on the tool-less synthesis turn is never shown, and nothing runs', async () => {
|
|
713
|
+
// After a terminal failure the synthesis turn offers no tools; a model that
|
|
714
|
+
// still wants to act writes the call as text (`runtime__delegate{"…"}`) and
|
|
715
|
+
// the turn did nothing. That raw call must never reach the user.
|
|
716
|
+
let delegated = false;
|
|
717
|
+
const narrated = 'runtime__delegate{"objective":"nettoyer le wiki"}';
|
|
718
|
+
const session = sessionBase({
|
|
719
|
+
runtime: { url: 'http://runtime.test' },
|
|
720
|
+
_runSkillWithinRun: async () => ({ ok: false, terminal: true, code: 'skill_not_found', availableSkills: [] }),
|
|
721
|
+
_delegateWithinRun: async () => { delegated = true; return { runId: 'bad' }; },
|
|
722
|
+
llm: {
|
|
723
|
+
async completeWithTools({ tools }) {
|
|
724
|
+
if (tools.some((tool) => tool.function?.name === 'classify_action_request')) {
|
|
725
|
+
return { content: null, message: { role: 'assistant', content: null }, tool_calls: [{ id: 'classify', type: 'function', function: { name: 'classify_action_request', arguments: '{"action":true}' } }] };
|
|
726
|
+
}
|
|
727
|
+
if (tools.length > 0) {
|
|
728
|
+
return { content: null, message: { role: 'assistant', content: null }, tool_calls: [
|
|
729
|
+
{ id: 'missing', type: 'function', function: { name: 'runtime__run_skill', arguments: '{"skillName":"missing","selectionKind":"explicit_name"}' } },
|
|
730
|
+
] };
|
|
731
|
+
}
|
|
732
|
+
return { content: narrated, message: { role: 'assistant', content: narrated }, tool_calls: null };
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
});
|
|
736
|
+
const result = await createAgentGraph().invoke({ input: 'nettoie le wiki', session });
|
|
737
|
+
assert.equal(delegated, false);
|
|
738
|
+
assert.match(result.response, /printed an internal tool request/);
|
|
739
|
+
assert.doesNotMatch(result.response, /runtime__delegate/);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
test('a recoverable skill refusal lets the delegate fallback run in the same turn', async () => {
|
|
743
|
+
// The observed defect: the model guessed `/diagnose`, the skill runner
|
|
744
|
+
// answered skill_not_found, and because that was terminal the tools were
|
|
745
|
+
// stripped from the next turn — the model then wrote
|
|
746
|
+
// `runtime__delegate{...}` as plain text and the turn did nothing. A guessed
|
|
747
|
+
// skill must be recoverable so the fallback can actually run.
|
|
748
|
+
let delegated = false;
|
|
749
|
+
const session = sessionBase({
|
|
750
|
+
runtime: { url: 'http://runtime.test' },
|
|
751
|
+
_runSkillWithinRun: async () => ({
|
|
752
|
+
ok: false,
|
|
753
|
+
terminal: false,
|
|
754
|
+
code: 'skill_not_found',
|
|
755
|
+
message: 'No skill named "/diagnose". Pass the exact name without a leading slash, or delegate the objective with runtime__delegate.',
|
|
756
|
+
availableSkills: ['diagnose'],
|
|
757
|
+
}),
|
|
758
|
+
_delegateWithinRun: async () => { delegated = true; return { runId: 'run-1', summary: { tasks: 1, agent: 'gateway' } }; },
|
|
759
|
+
llm: {
|
|
760
|
+
async completeWithTools({ tools }) {
|
|
761
|
+
if (tools.some((tool) => tool.function?.name === 'classify_action_request')) {
|
|
762
|
+
return { content: null, message: { role: 'assistant', content: null }, tool_calls: [{ id: 'classify', type: 'function', function: { name: 'classify_action_request', arguments: '{"action":true}' } }] };
|
|
763
|
+
}
|
|
764
|
+
return {
|
|
765
|
+
content: null, message: { role: 'assistant', content: null },
|
|
766
|
+
tool_calls: [
|
|
767
|
+
{ id: 'guess', type: 'function', function: { name: 'runtime__run_skill', arguments: '{"skillName":"/diagnose","arguments":{}}' } },
|
|
768
|
+
{ id: 'fallback', type: 'function', function: { name: 'runtime__delegate', arguments: '{"objective":"nettoyer le wiki, corriger les doublons et les affirmations non sourcées"}' } },
|
|
769
|
+
],
|
|
770
|
+
};
|
|
771
|
+
},
|
|
772
|
+
},
|
|
773
|
+
});
|
|
774
|
+
const result = await createAgentGraph().invoke({ input: 'nettoie le wiki, corrige les doublons et les affirmations non sourcées', session });
|
|
775
|
+
assert.equal(delegated, true);
|
|
776
|
+
assert.notEqual(result.terminalToolFailure, true);
|
|
777
|
+
});
|
|
778
|
+
|
|
709
779
|
test('tool argument normalization repairs only an unambiguous schema-compatible field name', () => {
|
|
710
780
|
const schema = {
|
|
711
781
|
type: 'object',
|
package/src/cli/wiki-manager.js
CHANGED
|
@@ -29,6 +29,8 @@ import { extractActivity, mergePolledActivity, parseJsonText, sessionActivities,
|
|
|
29
29
|
import { syncActivitiesToPlan, formatPlanStatus } from '../core/plan.js';
|
|
30
30
|
import { createAgentEvent, dispatchAgentEvent, reduceAgentEvents } from '../core/agentEvents.js';
|
|
31
31
|
import { runAgentTurn, runAgenticLoop } from '../core/agentLoop.js';
|
|
32
|
+
import { createDeltaCoalescer } from '../runtime/deltaCoalescer.js';
|
|
33
|
+
import { TERMINAL_STATUS_SET } from '../orchestrator/taskStatuses.js';
|
|
32
34
|
import { resolveCapabilityConcurrency } from '../orchestrator/scheduler.js';
|
|
33
35
|
import { capabilityRegistryForSession } from '../orchestrator/capabilityRegistry.js';
|
|
34
36
|
import { CapabilityUnavailableError, resolve as resolveCapability } from '../orchestrator/capabilityResolver.js';
|
|
@@ -827,7 +829,7 @@ async function runHeadless(argv, agent) {
|
|
|
827
829
|
// export finished, before the ingest had even started. The control queue is the
|
|
828
830
|
// only place where the whole chain is observable, so the wait is scoped to
|
|
829
831
|
// chainId and ends when every item of that chain is terminal.
|
|
830
|
-
const CHAIN_TERMINAL_STATUSES =
|
|
832
|
+
const CHAIN_TERMINAL_STATUSES = TERMINAL_STATUS_SET;
|
|
831
833
|
|
|
832
834
|
export async function waitForRuntimeChain(session, log, {
|
|
833
835
|
chainId,
|
|
@@ -1739,7 +1741,21 @@ async function runRuntime(argv, agent) {
|
|
|
1739
1741
|
workspace: context.workspace ?? ephemeral.workspace ?? null,
|
|
1740
1742
|
}));
|
|
1741
1743
|
const messages = conversationSeed({ agentProjection: persistedProjection }, input);
|
|
1744
|
+
// Streaming fragments are coalesced before they are persisted and pushed —
|
|
1745
|
+
// one synchronous SQLite insert (plus one SSE write) per token stalled the
|
|
1746
|
+
// event loop, freezing both chats (serve and ShellUI) while a long answer
|
|
1747
|
+
// was still being produced. Flushed before any non-delta event so ordering
|
|
1748
|
+
// and the final tail are preserved.
|
|
1749
|
+
const deltaCoalescer = createDeltaCoalescer((delta) => dispatchAgentEvent(ephemeral, createAgentEvent('assistant_delta', {
|
|
1750
|
+
origin: 'runtime_turn',
|
|
1751
|
+
turnId,
|
|
1752
|
+
workspace: context.workspace ?? null,
|
|
1753
|
+
payload: { delta },
|
|
1754
|
+
})), { intervalMs: 80 });
|
|
1742
1755
|
ephemeral._onAgentEvent = (event) => {
|
|
1756
|
+
// Never let a final message (or any other event) overtake the buffered
|
|
1757
|
+
// fragments that precede it: flush them first, in order.
|
|
1758
|
+
if (event.type !== 'assistant_delta' && event.type !== 'assistant_delta_reset') deltaCoalescer.flush();
|
|
1743
1759
|
const interactiveEvent = {
|
|
1744
1760
|
...event,
|
|
1745
1761
|
origin: 'runtime_turn',
|
|
@@ -1785,28 +1801,30 @@ async function runRuntime(argv, agent) {
|
|
|
1785
1801
|
response = await runHeadlessChatTurn(ephemeral, input, {
|
|
1786
1802
|
history,
|
|
1787
1803
|
onStep: ephemeral._onStep,
|
|
1788
|
-
// Fragments de réponse publiés au fil de l'eau
|
|
1789
|
-
// agrège dans la dernière
|
|
1790
|
-
// que `assistant_message`
|
|
1791
|
-
// voient la réponse s'écrire
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
})),
|
|
1804
|
+
// Fragments de réponse publiés au fil de l'eau, coalescés (voir
|
|
1805
|
+
// deltaCoalescer ci-dessus). Le réducteur les agrège dans la dernière
|
|
1806
|
+
// entrée de conversation (`assistant_delta`), que `assistant_message`
|
|
1807
|
+
// vient ensuite figer : les deux interfaces voient la réponse s'écrire
|
|
1808
|
+
// sans qu'un insert SQLite par token ne bloque le flux.
|
|
1809
|
+
onTextDelta: (delta) => deltaCoalescer.push(delta),
|
|
1810
|
+
onTextReset: () => {
|
|
1811
|
+
deltaCoalescer.reset();
|
|
1812
|
+
dispatchAgentEvent(ephemeral, createAgentEvent('assistant_delta_reset', {
|
|
1813
|
+
origin: 'runtime_turn',
|
|
1814
|
+
turnId,
|
|
1815
|
+
workspace: context.workspace ?? null,
|
|
1816
|
+
payload: {},
|
|
1817
|
+
}));
|
|
1818
|
+
},
|
|
1804
1819
|
openWikiPages,
|
|
1805
1820
|
});
|
|
1806
1821
|
} else {
|
|
1807
1822
|
ephemeral.openWikiPages = openWikiPages;
|
|
1808
1823
|
response = await runAgentTurn(agent, ephemeral, input, { messages, signal });
|
|
1809
1824
|
}
|
|
1825
|
+
// Flush the tail before the turn is finalized, then stop the timer.
|
|
1826
|
+
deltaCoalescer.flush();
|
|
1827
|
+
deltaCoalescer.dispose();
|
|
1810
1828
|
// Persist the artifact the turn may have opened/edited (template_write,
|
|
1811
1829
|
// template_read, …) back onto the long-lived session, so the next /turn —
|
|
1812
1830
|
// chat or agent — sees it. The ephemeral session is otherwise discarded.
|
package/src/core/agentEvents.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { normalizeActivity } from './activity.js';
|
|
2
|
+
import { cloneJson } from './json.js';
|
|
2
3
|
import { attachActivityToExistingPlan, syncActivitiesToPlan } from './plan.js';
|
|
3
4
|
import { applyPlanPatch, normalizePlanPatch, normalizePlanRevision, rebasePlanPatch } from './planPatch.js';
|
|
4
5
|
import { formatRuntimeLogPayload, isDispatchPlumbingLine, normalizeRuntimeLog, shortTaskLabel } from './runtimeLog.js';
|
|
5
6
|
import { projectSkillChains, TERMINAL as CONTROL_TERMINAL_STATUSES } from './skillChainView.js';
|
|
6
7
|
import { projectWorkflow } from './workflow.js';
|
|
7
8
|
import { validateContractInDev } from '../contracts/schemas.js';
|
|
8
|
-
import { isTerminal, isSuccessful, isUnknownStatus, normalizeTaskStatus } from '../orchestrator/taskStatuses.js';
|
|
9
|
+
import { isActive, isTerminal, isSuccessful, isUnknownStatus, normalizeTaskStatus } from '../orchestrator/taskStatuses.js';
|
|
9
10
|
|
|
10
11
|
const SESSION_PROJECTION_EVENTS = new Set([
|
|
11
12
|
'run_started',
|
|
@@ -291,6 +292,11 @@ export function applyAgentProjectionToSession(session, projection) {
|
|
|
291
292
|
} : session.productionActivity ?? null;
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
function hasRunningPlanStep(state) {
|
|
296
|
+
return (Array.isArray(state.plan) ? state.plan : [])
|
|
297
|
+
.some((step) => isActive(step?.status));
|
|
298
|
+
}
|
|
299
|
+
|
|
294
300
|
function applyEvent(state, event) {
|
|
295
301
|
switch (event.type) {
|
|
296
302
|
case 'run_started':
|
|
@@ -597,6 +603,13 @@ function applyEvent(state, event) {
|
|
|
597
603
|
reason: event.payload?.reason ?? null,
|
|
598
604
|
createdAt: event.ts,
|
|
599
605
|
});
|
|
606
|
+
// A run waiting for a human is not "running": showing it as running is
|
|
607
|
+
// how the chat could claim a rebuild was executing before anyone
|
|
608
|
+
// approved it. Mirror `run_pending_approval` (AGENTS.md: the run status
|
|
609
|
+
// is pending_approval while the decision is outstanding), but only when
|
|
610
|
+
// no task is actually executing — a parallel run may have work in flight
|
|
611
|
+
// while one branch waits.
|
|
612
|
+
if (!hasRunningPlanStep(state)) state.status = 'pending_approval';
|
|
600
613
|
return;
|
|
601
614
|
case 'approval.granted': {
|
|
602
615
|
const grant = {
|
|
@@ -616,6 +629,13 @@ function applyEvent(state, event) {
|
|
|
616
629
|
};
|
|
617
630
|
upsertApproval(state, grant);
|
|
618
631
|
markCoveredApprovalsApproved(state.approvals, grant, event.ts);
|
|
632
|
+
// The decision is in: the run goes back to running unless another
|
|
633
|
+
// approval is still outstanding (a run-scoped grant clears its covered
|
|
634
|
+
// ones, markCoveredApprovalsApproved above).
|
|
635
|
+
if (state.status === 'pending_approval'
|
|
636
|
+
&& !(state.approvals ?? []).some((approval) => approval.status === 'pending_approval')) {
|
|
637
|
+
state.status = 'running';
|
|
638
|
+
}
|
|
619
639
|
return;
|
|
620
640
|
}
|
|
621
641
|
case 'approval.rejected':
|
|
@@ -1240,6 +1260,3 @@ function sortedActivities(activities) {
|
|
|
1240
1260
|
.sort((a, b) => String(a.updatedAt ?? '').localeCompare(String(b.updatedAt ?? '')));
|
|
1241
1261
|
}
|
|
1242
1262
|
|
|
1243
|
-
function cloneJson(value) {
|
|
1244
|
-
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
1245
|
-
}
|
|
@@ -127,6 +127,40 @@ test('reduceAgentEvents: interactive (user) run_started clears state but is not
|
|
|
127
127
|
assert.notEqual(projection.status, 'running');
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
test('reduceAgentEvents: a run blocked on approval is not shown as running', () => {
|
|
131
|
+
// The chat claimed a rebuild was executing while its only task was still
|
|
132
|
+
// waiting for a human: the run status stayed 'running' through the per-task
|
|
133
|
+
// approval request. It must mirror run_pending_approval.
|
|
134
|
+
const projection = reduceAgentEvents([
|
|
135
|
+
createAgentEvent('run_started', { origin: 'runtime' }),
|
|
136
|
+
createAgentEvent('plan_set', { origin: 'tool', payload: { steps: ['Rebuild the concepts'] } }),
|
|
137
|
+
createAgentEvent('plan_step_updated', { origin: 'runtime', payload: { step: 1, status: 'waiting_approval' } }),
|
|
138
|
+
createAgentEvent('approval.requested', { origin: 'runtime', payload: { id: 'a1', scope: 'task', taskId: 't1' } }),
|
|
139
|
+
]);
|
|
140
|
+
assert.equal(projection.status, 'pending_approval');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('reduceAgentEvents: granting the approval puts the run back to running', () => {
|
|
144
|
+
const projection = reduceAgentEvents([
|
|
145
|
+
createAgentEvent('run_started', { origin: 'runtime' }),
|
|
146
|
+
createAgentEvent('plan_set', { origin: 'tool', payload: { steps: ['Rebuild the concepts'] } }),
|
|
147
|
+
createAgentEvent('plan_step_updated', { origin: 'runtime', payload: { step: 1, status: 'waiting_approval' } }),
|
|
148
|
+
createAgentEvent('approval.requested', { origin: 'runtime', payload: { id: 'a1', scope: 'task', taskId: 't1' } }),
|
|
149
|
+
createAgentEvent('approval.granted', { origin: 'runtime', payload: { id: 'a1', scope: 'task', taskId: 't1' } }),
|
|
150
|
+
]);
|
|
151
|
+
assert.equal(projection.status, 'running');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('reduceAgentEvents: an approval request does not hide a genuinely running task', () => {
|
|
155
|
+
const projection = reduceAgentEvents([
|
|
156
|
+
createAgentEvent('run_started', { origin: 'runtime' }),
|
|
157
|
+
createAgentEvent('plan_set', { origin: 'tool', payload: { steps: ['Export', 'Build'] } }),
|
|
158
|
+
createAgentEvent('plan_step_updated', { origin: 'runtime', payload: { step: 1, status: 'running' } }),
|
|
159
|
+
createAgentEvent('approval.requested', { origin: 'runtime', payload: { id: 'a1', scope: 'task', taskId: 't2' } }),
|
|
160
|
+
]);
|
|
161
|
+
assert.equal(projection.status, 'running');
|
|
162
|
+
});
|
|
163
|
+
|
|
130
164
|
test('reduceAgentEvents: tracks manual plan and step updates', () => {
|
|
131
165
|
const projection = reduceAgentEvents([
|
|
132
166
|
createAgentEvent('plan_set', {
|
package/src/core/buildInfo.json
CHANGED
package/src/core/googleGrants.js
CHANGED
package/src/core/json.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Deep clone via a JSON round-trip; `null`/`undefined` pass through.
|
|
3
|
+
*
|
|
4
|
+
* Three modules had grown their own byte-identical copy (agentEvents,
|
|
5
|
+
* agentRegistry, planValidator). One implementation, imported.
|
|
6
|
+
*/
|
|
7
|
+
export function cloneJson(value) {
|
|
8
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
9
|
+
}
|
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.97';
|
|
5
5
|
|
|
6
6
|
function envValue(key) {
|
|
7
7
|
const filePath = managerEnvFile();
|
package/src/core/plan.js
CHANGED
|
@@ -48,10 +48,6 @@ export function extractHeadlessPlan(text) {
|
|
|
48
48
|
return steps;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export function matchCompletedToPlan(plan, completed) {
|
|
52
|
-
if (!plan) return;
|
|
53
|
-
syncActivitiesToPlan(plan, completed.filter((activity) => activity.terminal));
|
|
54
|
-
}
|
|
55
51
|
|
|
56
52
|
export function syncActivitiesToPlan(plan, activities) {
|
|
57
53
|
if (!plan) return;
|