@dotdrelle/wiki-manager 0.12.1 → 0.12.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -8
- package/bin/wiki-manager +23 -12
- package/docker-compose.yml +0 -1
- package/mcp.endpoints.example.json +0 -6
- package/package.json +3 -2
- package/src/activity/activityAggregator.js +34 -3
- package/src/activity/activityAggregator.test.js +32 -0
- package/src/agent/graph.js +272 -22
- package/src/agent/graph.test.js +357 -1
- package/src/cli/wiki-manager.js +33 -2
- package/src/commands/slash.js +74 -1
- package/src/commands/slash.test.js +36 -0
- package/src/core/activity.js +4 -0
- package/src/core/activity.test.js +9 -1
- package/src/core/agentEvents.js +30 -0
- package/src/core/agentEvents.test.js +42 -1
- package/src/core/buildInfo.js +58 -0
- package/src/core/buildInfo.json +4 -0
- package/src/core/buildInfo.test.js +14 -0
- package/src/core/env.js +30 -1
- package/src/core/mcp.js +21 -1
- package/src/core/mcp.test.js +25 -0
- package/src/core/runtimeLog.js +6 -1
- package/src/core/runtimeLog.test.js +3 -1
- package/src/runtime/client.js +22 -0
- package/src/runtime/controlMessages.js +2 -2
- package/src/runtime/recoveryManager.js +54 -0
- package/src/runtime/recoveryManager.test.js +73 -0
- package/src/runtime/runner.js +49 -13
- package/src/runtime/runner.test.js +196 -0
- package/src/runtime/server.js +67 -8
- package/src/runtime/server.test.js +223 -6
- package/src/runtime/store.js +48 -3
- package/src/runtime/store.test.js +32 -0
- package/src/runtime/supervisor.js +77 -1
- package/src/shell/RightPane.tsx +124 -31
- package/src/shell/SetupWizard.tsx +13 -1
- package/src/shell/repl.js +78 -9
- package/src/shell/repl.test.js +114 -1
- package/src/shell/tui.tsx +4 -1
- package/src/shell/useAgent.ts +32 -3
- package/src/shell/useSession.ts +14 -1
- package/wiki-workspace +33 -0
package/src/shell/useAgent.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSignal } from 'solid-js';
|
|
2
2
|
import { postRuntimeCancel } from '../runtime/client.js';
|
|
3
|
-
import { conversationMessages, recordRuntimeUnavailableAgentInput, runLine, submitRuntimeRun } from './repl.js';
|
|
3
|
+
import { conversationMessages, recordRuntimeUnavailableAgentInput, runLine, shouldHandleFreeTextLocally, submitRuntimeRun } from './repl.js';
|
|
4
4
|
|
|
5
5
|
export function useAgent(props: { agent: unknown; packageJson: Record<string, unknown>; session: Record<string, any>; chatMode: () => boolean; runtimeUrl?: string | null; runtimeUnavailableReason?: string | null; refresh: () => void; addLog: (line: string) => void; onRuntimeAccepted?: () => void }) {
|
|
6
6
|
const [busy, setBusy] = createSignal(false);
|
|
@@ -18,7 +18,17 @@ export function useAgent(props: { agent: unknown; packageJson: Record<string, un
|
|
|
18
18
|
props.addLog(`input: ${trimmed}`);
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
|
-
|
|
21
|
+
// Questions and small talk are answered by the local agent: the reply
|
|
22
|
+
// shows up in the chat immediately and no runtime run (plan, SQLite
|
|
23
|
+
// state, replans) is created for a sentence that only needed an answer.
|
|
24
|
+
// Actions still go to the runtime below.
|
|
25
|
+
const freeTextRouting = (props.runtimeUrl && !props.chatMode() && !trimmed.startsWith('/'))
|
|
26
|
+
? shouldHandleFreeTextLocally(trimmed, props.session)
|
|
27
|
+
: null;
|
|
28
|
+
if (props.runtimeUrl && !props.chatMode() && !trimmed.startsWith('/') && !freeTextRouting?.local) {
|
|
29
|
+
if (freeTextRouting?.fallbackReason) {
|
|
30
|
+
props.addLog(`runtime: ${freeTextRouting.fallbackReason}, routing to runtime run`);
|
|
31
|
+
}
|
|
22
32
|
// Marked _pending so mergeRuntimeConversation (useSession.ts) can
|
|
23
33
|
// confirm this exact entry instead of pushing a second copy once the
|
|
24
34
|
// same user message comes back from the runtime's own /state.
|
|
@@ -29,6 +39,13 @@ export function useAgent(props: { agent: unknown; packageJson: Record<string, un
|
|
|
29
39
|
});
|
|
30
40
|
if (outcome.kind === 'accepted') {
|
|
31
41
|
props.onRuntimeAccepted?.();
|
|
42
|
+
const runId = (outcome as any).result?.runId ?? null;
|
|
43
|
+
// Light, immediate feedback in the chat: without it an accepted run
|
|
44
|
+
// is invisible until its first activity lands in the side panels.
|
|
45
|
+
conversationMessages(props.session).push({
|
|
46
|
+
role: 'command',
|
|
47
|
+
content: `▶ Run accepté${runId ? ` (${String(runId).slice(0, 8)})` : ''} — progression dans le panneau Activity, réponse ici à la fin du run.`,
|
|
48
|
+
});
|
|
32
49
|
props.addLog('runtime: run accepted');
|
|
33
50
|
} else if (outcome.kind === 'queued') {
|
|
34
51
|
// The server localizes control-lane acknowledgements from the
|
|
@@ -37,6 +54,11 @@ export function useAgent(props: { agent: unknown; packageJson: Record<string, un
|
|
|
37
54
|
const explanation = (outcome as any).result?.explanation ?? 'Request added to the queue.';
|
|
38
55
|
conversationMessages(props.session).push({ role: 'command', content: String(explanation) });
|
|
39
56
|
props.addLog('runtime: control queued');
|
|
57
|
+
} else if ((outcome as any).result?.explanation) {
|
|
58
|
+
// Control-lane kinds (cancel / approve / observe / modify_run…):
|
|
59
|
+
// surface the server's localized explanation instead of an error.
|
|
60
|
+
conversationMessages(props.session).push({ role: 'command', content: String((outcome as any).result.explanation) });
|
|
61
|
+
props.addLog(`runtime: ${outcome.kind}`);
|
|
40
62
|
} else {
|
|
41
63
|
conversationMessages(props.session).push({ role: 'command', content: `Runtime error: ${outcome.message}` });
|
|
42
64
|
props.addLog(`runtime error: ${outcome.message}`);
|
|
@@ -44,7 +66,7 @@ export function useAgent(props: { agent: unknown; packageJson: Record<string, un
|
|
|
44
66
|
props.refresh();
|
|
45
67
|
return { exit: false, runtime: true };
|
|
46
68
|
}
|
|
47
|
-
if (!props.chatMode() && !trimmed.startsWith('/')) {
|
|
69
|
+
if (!props.chatMode() && !trimmed.startsWith('/') && !freeTextRouting?.local) {
|
|
48
70
|
const message = recordRuntimeUnavailableAgentInput(props.session, trimmed, {
|
|
49
71
|
error: props.runtimeUnavailableReason ?? 'runtime introuvable',
|
|
50
72
|
});
|
|
@@ -52,6 +74,9 @@ export function useAgent(props: { agent: unknown; packageJson: Record<string, un
|
|
|
52
74
|
props.refresh();
|
|
53
75
|
return { exit: false, runtimeUnavailable: true };
|
|
54
76
|
}
|
|
77
|
+
if (freeTextRouting?.local) {
|
|
78
|
+
props.addLog(`agent: ${freeTextRouting.classification.kind} handled locally`);
|
|
79
|
+
}
|
|
55
80
|
const result = await runLine(trimmed, {
|
|
56
81
|
agent: props.agent,
|
|
57
82
|
packageJson: props.packageJson,
|
|
@@ -59,6 +84,10 @@ export function useAgent(props: { agent: unknown; packageJson: Record<string, un
|
|
|
59
84
|
onUpdate: props.refresh,
|
|
60
85
|
onStep: props.addLog,
|
|
61
86
|
chatMode: props.chatMode(),
|
|
87
|
+
// Without this, slash commands that target the runtime (/run kill,
|
|
88
|
+
// /run cancel, /run status) reported "Runtime unavailable" in the
|
|
89
|
+
// TUI even while the status bar showed "runtime: connected".
|
|
90
|
+
runtime: props.runtimeUrl ? { url: props.runtimeUrl } : null,
|
|
62
91
|
});
|
|
63
92
|
props.refresh();
|
|
64
93
|
return result;
|
package/src/shell/useSession.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js';
|
|
4
4
|
import { formatMcpToolResult, callMcpTool } from '../core/mcp.js';
|
|
5
|
+
import { versionWithBuild } from '../core/buildInfo.js';
|
|
5
6
|
import { extractActivity, parseJsonText, sessionActivities } from '../core/activity.js';
|
|
6
7
|
import { formatPlanStatus, formatCompletedActivities, formatPlanStep } from '../core/plan.js';
|
|
7
8
|
import { createAgentEvent, dispatchAgentEvent } from '../core/agentEvents.js';
|
|
@@ -47,6 +48,12 @@ function buildContinuationPrompt(session: any, completed: any[]): string {
|
|
|
47
48
|
|
|
48
49
|
export function useSession(props: { agent: unknown; packageJson: Record<string, unknown>; runtime?: any }) {
|
|
49
50
|
const session = createSession();
|
|
51
|
+
// Donna's runtime__kill/cancel/status/approve/enqueue tools (graph.js)
|
|
52
|
+
// read session.runtime.url directly — without this the OpenTUI session
|
|
53
|
+
// never had it set (only the legacy runShell path did), so those tools
|
|
54
|
+
// reported "Runtime not connected" even with the runtime up and the
|
|
55
|
+
// status bar showing "connected".
|
|
56
|
+
(session as any).runtime = props.runtime ?? null;
|
|
50
57
|
const [version, setVersion] = createSignal(0);
|
|
51
58
|
const [logs, setLogs] = createSignal<string[]>([]);
|
|
52
59
|
const [runtimeState, setRuntimeState] = createSignal<any | null>(null);
|
|
@@ -139,7 +146,7 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
|
|
|
139
146
|
const statusLine = createMemo(() => {
|
|
140
147
|
version();
|
|
141
148
|
return [
|
|
142
|
-
`wiki-manager ${props.packageJson
|
|
149
|
+
`wiki-manager ${versionWithBuild(props.packageJson)}`.trim(),
|
|
143
150
|
session.workspace ? session.workspace : 'no workspace',
|
|
144
151
|
session.wikirc?.profile ? session.wikirc.profile : 'no wikirc',
|
|
145
152
|
session.language ? session.language : 'no language',
|
|
@@ -337,6 +344,12 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
|
|
|
337
344
|
const target = conversationMessages(session);
|
|
338
345
|
const backed = runtimeConversationRefsByWorkspace.get(workspace) ?? [];
|
|
339
346
|
runtimeConversationRefsByWorkspace.set(workspace, backed);
|
|
347
|
+
// The merge is index-aligned with the runtime conversation array. If that
|
|
348
|
+
// array got SHORTER (runtime restart, projection reset), keeping stale
|
|
349
|
+
// refs would make every new runtime entry silently overwrite an old
|
|
350
|
+
// backed entry instead of appearing in the chat. Drop the refs and let
|
|
351
|
+
// the new conversation append cleanly.
|
|
352
|
+
if (runtimeConversation.length < backed.length) backed.length = 0;
|
|
340
353
|
if (backed.length === 0 && target.length === 0 && !runtimeConversation.some((message: any) => message?._pending)) {
|
|
341
354
|
backed.push(...runtimeConversation.map((message: any) => ({
|
|
342
355
|
role: message.role === 'assistant' ? 'donna' : message.role,
|
package/wiki-workspace
CHANGED
|
@@ -228,6 +228,12 @@ agents_compose() {
|
|
|
228
228
|
shift
|
|
229
229
|
fi
|
|
230
230
|
[[ $# -eq 0 ]] || die "agents up accepts only --pull"
|
|
231
|
+
ensure_agent_tokens
|
|
232
|
+
ensure_endpoints_file
|
|
233
|
+
# The .env may have just been created above — recompute the env-file
|
|
234
|
+
# flag so freshly generated tokens reach the containers on first run.
|
|
235
|
+
compose_env_args=()
|
|
236
|
+
[[ -f "$MANAGER_ENV_FILE" ]] && compose_env_args+=(--env-file "$MANAGER_ENV_FILE")
|
|
231
237
|
mkdir -p "$agents_data_dir/cme" "$agents_data_dir/documents/input" "$agents_data_dir/documents/output" "$agents_data_dir/documents/uploads"
|
|
232
238
|
mkdir -p "$workspaces_root"
|
|
233
239
|
$do_pull && _agents_dc pull
|
|
@@ -382,6 +388,33 @@ ensure_runtime_node() {
|
|
|
382
388
|
[[ "$major" =~ ^[0-9]+$ && "$major" -ge 22 ]] || die "runtime requires Node.js 22+ for node:sqlite; $node_bin is Node $version"
|
|
383
389
|
}
|
|
384
390
|
|
|
391
|
+
ensure_agent_tokens() {
|
|
392
|
+
# The external agents (cme, documents, mailer) authenticate with tokens
|
|
393
|
+
# from the manager .env — generate any missing one, exactly like the
|
|
394
|
+
# workspace and runtime tokens are generated. Leaving them empty made a
|
|
395
|
+
# fresh install start agents nobody could talk to.
|
|
396
|
+
local key token
|
|
397
|
+
for key in CME_MCP_AUTH_TOKEN DOCUMENTS_MCP_AUTH_TOKEN MAILER_MCP_AUTH_TOKEN; do
|
|
398
|
+
token="$(env_value "$MANAGER_ENV_FILE" "$key" "")"
|
|
399
|
+
if [[ -z "$token" ]]; then
|
|
400
|
+
token="$(generate_token)"
|
|
401
|
+
mkdir -p "$(dirname "$MANAGER_ENV_FILE")"
|
|
402
|
+
set_env_value "$MANAGER_ENV_FILE" "$key" "$token"
|
|
403
|
+
printf 'Generated %s in %s\n' "$key" "$MANAGER_ENV_FILE"
|
|
404
|
+
fi
|
|
405
|
+
done
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
ensure_endpoints_file() {
|
|
409
|
+
# Without mcp.endpoints.json the shell never connects to the agents it
|
|
410
|
+
# just started (and `agents status` refuses to run). Seed it from the
|
|
411
|
+
# packaged example; the \${VAR} placeholders resolve against the .env.
|
|
412
|
+
if [[ ! -f "$MANAGER_ENDPOINTS_FILE" && -f "$ROOT_DIR/mcp.endpoints.example.json" ]]; then
|
|
413
|
+
cp "$ROOT_DIR/mcp.endpoints.example.json" "$MANAGER_ENDPOINTS_FILE"
|
|
414
|
+
printf 'Created %s from packaged example\n' "$MANAGER_ENDPOINTS_FILE"
|
|
415
|
+
fi
|
|
416
|
+
}
|
|
417
|
+
|
|
385
418
|
ensure_manager_runtime_token() {
|
|
386
419
|
local token
|
|
387
420
|
token="$(env_value "$MANAGER_ENV_FILE" WIKI_MANAGER_RUNTIME_TOKEN "")"
|