@dotdrelle/wiki-manager 0.15.66 → 0.15.70

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.
Files changed (50) hide show
  1. package/.env.example +10 -3
  2. package/README.md +54 -0
  3. package/agent-runtimes.example.json +68 -0
  4. package/agents.docker-compose.yml +35 -1
  5. package/docker-compose.yml +3 -3
  6. package/package.json +3 -2
  7. package/src/agent/graph.js +12 -11
  8. package/src/agent/skillRecursion.test.js +13 -12
  9. package/src/cli/wiki-manager.js +124 -36
  10. package/src/commands/slash.js +38 -3
  11. package/src/contracts/schemas.js +67 -0
  12. package/src/core/activity.js +5 -0
  13. package/src/core/agentEvents.js +18 -1
  14. package/src/core/buildInfo.json +2 -2
  15. package/src/core/dockerCompose.test.js +8 -40
  16. package/src/core/env.js +14 -0
  17. package/src/core/env.test.js +19 -0
  18. package/src/core/googleGrants.test.js +1 -1
  19. package/src/core/mcp.js +1 -1
  20. package/src/core/runtimeEventAdapter.js +81 -0
  21. package/src/core/runtimeEventAdapter.test.js +61 -0
  22. package/src/core/skillChainView.test.js +2 -2
  23. package/src/core/skillCompiler.test.js +1 -1
  24. package/src/core/skillInvocation.js +13 -8
  25. package/src/core/startupCheck.js +58 -0
  26. package/src/core/startupCheck.test.js +29 -1
  27. package/src/orchestrator/agentRegistry.js +1 -22
  28. package/src/orchestrator/assignmentManager.js +16 -4
  29. package/src/orchestrator/capabilityRegistry.js +8 -1
  30. package/src/orchestrator/dispatcher.js +361 -2
  31. package/src/orchestrator/dispatcher.test.js +112 -1
  32. package/src/orchestrator/objectiveResolver.js +10 -6
  33. package/src/orchestrator/objectiveResolver.test.js +26 -27
  34. package/src/orchestrator/providers/deepAgentsProvider.js +168 -0
  35. package/src/orchestrator/providers/deepAgentsProvider.test.js +178 -0
  36. package/src/orchestrator/providers/dispatcherExternalRuntime.test.js +409 -0
  37. package/src/orchestrator/providers/fakeRuntimeProvider.js +164 -0
  38. package/src/orchestrator/providers/fakeRuntimeProvider.test.js +201 -0
  39. package/src/orchestrator/providers/runtimeProvider.js +101 -0
  40. package/src/orchestrator/providers/runtimeProviders.js +325 -0
  41. package/src/orchestrator/providers/runtimeProviders.test.js +361 -0
  42. package/src/orchestrator/resultAggregator.js +35 -2
  43. package/src/orchestrator/resultAggregator.test.js +62 -0
  44. package/src/runtime/recoveryManager.js +70 -5
  45. package/src/runtime/skillChain.e2e.test.js +2 -2
  46. package/src/runtime/supervisor.js +5 -10
  47. package/src/shell/RightPane.tsx +9 -1
  48. package/src/shell/StartupScreen.tsx +44 -7
  49. package/src/shell/repl.test.js +13 -0
  50. package/wiki-workspace +19 -3
@@ -263,7 +263,11 @@ export function PlanPanel(props: { plan: PlanStep[]; width: number; jobName?: st
263
263
 
264
264
  export function ActivityPanel(props: { activities: any[]; width: number }) {
265
265
  const lineWidth = () => Math.max(8, props.width - 2);
266
- const visible = () => props.activities.slice().reverse();
266
+ // session.activities is never pruned within a run (see core/agentEvents.js),
267
+ // so a long batch job's activity count is unbounded — render only the most
268
+ // recent slice, memoized like LogPanel's allLines, instead of copying and
269
+ // re-wrapping the entire lifetime history on every reactive tick.
270
+ const visible = createMemo(() => props.activities.slice(-200).reverse());
267
271
  return (
268
272
  <box flexGrow={1} flexDirection="column" paddingX={1} backgroundColor="#111318">
269
273
  <text width={lineWidth()} fg="#D6DEE8" content="Activity" />
@@ -340,6 +344,10 @@ type LogSegment = { text: string; fg: string };
340
344
  // Continuation lines of a wrapped entry are indented and dimmed so each
341
345
  // entry reads as one visual block instead of an undifferentiated wall.
342
346
  function logMessageColor(message: string): string {
347
+ // A doctor summary with ZERO errors is a warning by construction
348
+ // ("⚠ 0 error(s), 2 warning(s)") — amber, never red, even though it
349
+ // mentions the word "error".
350
+ if (/(?<!\d)0 error\(s\)/i.test(message)) return '#FBBF24';
343
351
  if (/\b(?:trace:\s*)?WARN\b/i.test(message)) return '#FBBF24';
344
352
  if (/\b(error|failed|exception|unavailable|introuvable|HTTP 4\d\d|HTTP 5\d\d)\b/i.test(message)) return '#F38BA8';
345
353
  if (/\b(warn|warning|avertissement|fallback|retry|expired|stale)\b/i.test(message)) return '#FBBF24';
@@ -238,9 +238,36 @@ export function StartupScreen(props: {
238
238
  });
239
239
 
240
240
  const preflightChecks = createMemo(() => props.preflight?.checks ?? []);
241
+ const checkByKind = createMemo(() => {
242
+ const map = new Map<string, any>();
243
+ for (const check of preflightChecks()) map.set(check.kind, check);
244
+ return map;
245
+ });
246
+ // Docker and Internet leave the check list: they are ambient state, shown in
247
+ // the header (top right), not among the startup rows.
248
+ const dockerCheck = createMemo(() => checkByKind().get('docker'));
249
+ const internetCheck = createMemo(() => checkByKind().get('internet'));
250
+ const listChecks = createMemo(() => {
251
+ const order = ['workspace', 'runtime', 'agentic', 'agents', 'containers', 'mcp'];
252
+ const seen = new Set<string>(['docker', 'internet']);
253
+ const ordered: Array<{ kind: string; ok?: boolean; skipped?: boolean; pending?: boolean; detail?: string }> = [];
254
+ for (const kind of order) {
255
+ const check = checkByKind().get(kind);
256
+ if (!check) continue;
257
+ // An optional engine is not part of the startup story: when nothing is
258
+ // enabled, its row disappears instead of showing a misleading green ✓.
259
+ if (kind === 'agentic' && check.skipped) continue;
260
+ ordered.push(check);
261
+ seen.add(kind);
262
+ }
263
+ for (const check of preflightChecks()) {
264
+ if (!seen.has(check.kind)) ordered.push(check);
265
+ }
266
+ return ordered;
267
+ });
241
268
  const checkLabel = (kind: string) => ({
242
269
  docker: 'Docker', internet: 'Internet', agents: 'Agents', workspace: 'Workspaces',
243
- containers: 'Containers', mcp: 'MCP', runtime: 'Runtime',
270
+ containers: 'Containers', mcp: 'MCP', agentic: 'Agentic runtime', runtime: 'Runtime',
244
271
  } as Record<string, string>)[kind] ?? kind;
245
272
 
246
273
  const subtitle = createMemo(() => {
@@ -281,10 +308,20 @@ export function StartupScreen(props: {
281
308
  overflow="hidden"
282
309
  >
283
310
  <box height={1} flexDirection="row">
284
- <text fg="#8BD5CA" content={fit(`DONNA v${props.version}`, Math.floor(innerWidth() * 0.5))} />
285
- <text fg={statusColor()} content={fit(` ${props.preflightBusy ? '◐' : statusDot(props.preflight?.status === 'ready')} ${status()}`, Math.floor(innerWidth() * 0.45))} />
311
+ <text fg="#8BD5CA" content={fit(`DONNA v${props.version}`, Math.floor(innerWidth() * 0.4))} />
312
+ <text fg={statusColor()} content={fit(` ${props.preflightBusy ? '◐' : statusDot(props.preflight?.status === 'ready')} ${status()}`, Math.floor(innerWidth() * 0.32))} />
313
+ <box flexGrow={1} />
314
+ <text fg="#7F8C8D" content={dockerCheck() ? fit(`Docker — ${dockerCheck()?.detail ?? '—'}`, Math.floor(innerWidth() * 0.28)) : ''} />
315
+ </box>
316
+ <box height={1} flexDirection="row">
317
+ <text height={1} fg="#7F8C8D" content={fit(subtitle(), Math.floor(innerWidth() * 0.6))} />
318
+ <box flexGrow={1} />
319
+ <text
320
+ height={1}
321
+ fg={internetCheck() ? (internetCheck()?.ok ? '#8BD5CA' : '#FBBF24') : '#7F8C8D'}
322
+ content={internetCheck() ? fit(`Internet — ${internetCheck()?.ok ? 'OK' : 'KO'}`, Math.floor(innerWidth() * 0.28)) : ''}
323
+ />
286
324
  </box>
287
- <text height={1} fg="#7F8C8D" content={fit(subtitle(), innerWidth())} />
288
325
  <text height={1}>{''}</text>
289
326
  <box
290
327
  height={8}
@@ -295,7 +332,7 @@ export function StartupScreen(props: {
295
332
  overflow="hidden"
296
333
  >
297
334
  <text fg="#d6a85f">{DONNA_LOGO}</text>
298
- <text fg="#888888">Intelligent workspace</text>
335
+ <text fg="#888888">Intelligent Agentic workspace(s)</text>
299
336
  </box>
300
337
  <text height={1}>{''}</text>
301
338
  <text height={1} fg="#7F8C8D" content={menuTitle()} />
@@ -320,8 +357,8 @@ export function StartupScreen(props: {
320
357
  </For>
321
358
  </box>
322
359
  <text height={1}>{''}</text>
323
- <box height={Math.max(3, preflightChecks().length)} flexDirection="column" border={['left']} borderStyle="heavy" borderColor="#5DADE2" paddingX={1} overflow="hidden">
324
- <For each={preflightChecks().length ? preflightChecks() : [
360
+ <box height={Math.max(3, listChecks().length)} flexDirection="column" border={['left']} borderStyle="heavy" borderColor="#5DADE2" paddingX={1} overflow="hidden">
361
+ <For each={listChecks().length ? listChecks() : [
325
362
  { kind: 'workspace', ok: props.wikiReady, detail: props.wikiReady ? 'default profile ready' : 'init required' },
326
363
  { kind: 'mcp', ok: props.connectedMcpServers > 0, detail: `${props.connectedMcpServers} server(s) configured` },
327
364
  { kind: 'llm', ok: Boolean(props.model), detail: props.model || 'not configured' },
@@ -173,6 +173,19 @@ test('Flow/Trace does not repeat the runtime source prefix on every line', async
173
173
  assert.match(entryRenderer, /prefix\.push\(\{ text: `\$\{parts\.time\} /);
174
174
  });
175
175
 
176
+ test('a doctor summary with a nonzero error count is colored as an error, not a warning', async () => {
177
+ const source = await readFile(new URL('./RightPane.tsx', import.meta.url), 'utf8');
178
+ const colorFn = source.slice(
179
+ source.indexOf('function logMessageColor'),
180
+ source.indexOf('function logRenderLines'),
181
+ );
182
+ // The "0 error(s)" amber shortcut must be digit-anchored: "10 error(s)"
183
+ // ends in "0 error(s)" too, and an unanchored test painted a 10-error
184
+ // doctor failure the same colour as a clean run.
185
+ assert.match(colorFn, /\(\?<!\\d\)0 error\\\(s\\\)/);
186
+ assert.doesNotMatch(colorFn, /\/0 error\\\(s\\\)\/i\.test/);
187
+ });
188
+
176
189
  test('runtime logs have a separator and a concise Runtime tab label', async () => {
177
190
  const source = await readFile(new URL('./RightPane.tsx', import.meta.url), 'utf8');
178
191
  const logPanel = source.slice(
package/wiki-workspace CHANGED
@@ -299,7 +299,12 @@ agents_compose() {
299
299
  local cacert_args=()
300
300
  local profile_args=()
301
301
  connectors_enabled && profile_args+=(--profile connectors)
302
- WORKSPACES_ROOT="$workspaces_root" AGENTS_DATA_DIR="$agents_data_dir" \
302
+ gateway_enabled && profile_args+=(--profile gateway)
303
+ # The gateway mounts the MANAGER's agent-runtimes.json — absolute path:
304
+ # compose resolves relative volume paths against the compose file's
305
+ # directory (the installed package), never the operator's state dir.
306
+ local agent_runtimes_file="$(dirname "$MANAGER_ENV_FILE")/agent-runtimes.json"
307
+ WORKSPACES_ROOT="$workspaces_root" AGENTS_DATA_DIR="$agents_data_dir" AGENT_RUNTIMES_FILE="$agent_runtimes_file" \
303
308
  read_lines_into_array cacert_args cacert_compose_args "$agents_compose_file" "agents.cacert.compose.yml"
304
309
  # User-owned override: optional external agents, proxy passthrough and
305
310
  # any local fix live in a file the user maintains under .wiki/compose —
@@ -307,7 +312,7 @@ agents_compose() {
307
312
  local override_args=()
308
313
  local user_override="$MANAGER_COMPOSE_DIR/agents.docker-compose.override.yml"
309
314
  [[ -f "$user_override" ]] && override_args+=(-f "$user_override")
310
- WORKSPACES_ROOT="$workspaces_root" AGENTS_DATA_DIR="$agents_data_dir" \
315
+ WORKSPACES_ROOT="$workspaces_root" AGENTS_DATA_DIR="$agents_data_dir" AGENT_RUNTIMES_FILE="$agent_runtimes_file" \
311
316
  COMPOSE_PROFILES= \
312
317
  docker compose --project-directory "$DEFAULT_MANAGER_DIR" \
313
318
  ${compose_env_args[@]+"${compose_env_args[@]}"} -f "$agents_compose_file" ${override_args[@]+"${override_args[@]}"} ${cacert_args[@]+"${cacert_args[@]}"} ${profile_args[@]+"${profile_args[@]}"} -p wiki-agents "$@"
@@ -331,6 +336,7 @@ agents_compose() {
331
336
  [[ -f "$MANAGER_ENV_FILE" ]] && compose_env_args+=(--env-file "$MANAGER_ENV_FILE")
332
337
  mkdir -p "$agents_data_dir/cme" "$agents_data_dir/documents/input" "$agents_data_dir/documents/output" "$agents_data_dir/documents/uploads"
333
338
  connectors_enabled && mkdir -p "$agents_data_dir/connectors"
339
+ gateway_enabled && mkdir -p "$agents_data_dir/gateway"
334
340
  mkdir -p "$workspaces_root"
335
341
  $do_pull && _agents_dc pull
336
342
  local up_args=(up -d)
@@ -345,6 +351,7 @@ agents_compose() {
345
351
  printf 'Agents: cme=:%s documents=:%s' \
346
352
  "${CME_MCP_PORT:-3336}" "${DOCUMENTS_MCP_PORT:-3337}"
347
353
  connectors_enabled && printf ' connectors=:%s' "${CONNECTORS_MCP_PORT:-3338}"
354
+ gateway_enabled && printf ' gateway=:%s' "${GATEWAY_PORT:-7789}"
348
355
  printf '\n'
349
356
  [[ ! -f "$MANAGER_COMPOSE_DIR/agents.docker-compose.override.yml" ]] || printf 'User override: %s\n' "$MANAGER_COMPOSE_DIR/agents.docker-compose.override.yml"
350
357
  ;;
@@ -509,7 +516,7 @@ ensure_agent_tokens() {
509
516
  # workspace and runtime tokens are generated. Leaving them empty made a
510
517
  # fresh install start agents nobody could talk to.
511
518
  local key token
512
- for key in CME_MCP_AUTH_TOKEN DOCUMENTS_MCP_AUTH_TOKEN; do
519
+ for key in CME_MCP_AUTH_TOKEN DOCUMENTS_MCP_AUTH_TOKEN GATEWAY_AUTH_TOKEN; do
513
520
  token="$(env_value "$MANAGER_ENV_FILE" "$key" "")"
514
521
  if [[ -z "$token" ]]; then
515
522
  token="$(generate_token)"
@@ -558,6 +565,15 @@ connectors_enabled() {
558
565
  esac
559
566
  }
560
567
 
568
+ gateway_enabled() {
569
+ local enabled
570
+ enabled="$(env_value "$MANAGER_ENV_FILE" GATEWAY_ENABLED "${GATEWAY_ENABLED:-true}")"
571
+ case "$enabled" in
572
+ 1|[Tt][Rr][Uu][Ee]|[Yy][Ee][Ss]|[Oo][Nn]) return 0 ;;
573
+ *) return 1 ;;
574
+ esac
575
+ }
576
+
561
577
  ensure_endpoints_file() {
562
578
  # Without mcp.endpoints.json the shell never connects to the agents it
563
579
  # just started (and `agents status` refuses to run). Seed it from the