arkaos 4.20.0 → 4.22.0

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 (80) hide show
  1. package/README.md +1 -1
  2. package/VERSION +1 -1
  3. package/arka/SKILL.md +2 -1
  4. package/config/claude-agents/marta-cqo.md +9 -0
  5. package/config/standards/{claude-md-overlays → stack-rules}/laravel.md +11 -0
  6. package/config/standards/{claude-md-overlays → stack-rules}/node.md +10 -1
  7. package/config/standards/{claude-md-overlays → stack-rules}/nuxt.md +12 -1
  8. package/config/standards/stack-rules/php.md +13 -0
  9. package/config/standards/{claude-md-overlays → stack-rules}/python.md +5 -0
  10. package/config/standards/stack-rules/react.md +14 -0
  11. package/config/standards/stack-rules/vue.md +13 -0
  12. package/core/cognition/evolve.py +299 -0
  13. package/core/cognition/evolve_cli.py +55 -0
  14. package/core/governance/stop_lint.py +266 -0
  15. package/core/governance/stop_lint_telemetry.py +132 -0
  16. package/core/governance/tool_loop_check.py +111 -0
  17. package/core/hooks/stop.py +73 -22
  18. package/core/runtime/pricing.py +53 -19
  19. package/core/synapse/layers.py +49 -476
  20. package/core/synapse/layers_base.py +99 -0
  21. package/core/synapse/layers_kb.py +395 -0
  22. package/core/sync/content_syncer.py +79 -10
  23. package/dashboard/app/components/AgentDependencyGraph.vue +5 -5
  24. package/dashboard/app/components/AgentEditDrawer.vue +120 -52
  25. package/dashboard/app/components/AgentSuggestionsCard.vue +4 -2
  26. package/dashboard/app/components/ConfirmDialog.vue +2 -2
  27. package/dashboard/app/components/DashboardState.vue +5 -3
  28. package/dashboard/app/components/GlobalSearch.vue +11 -7
  29. package/dashboard/app/components/KeyboardShortcutsHelp.vue +8 -6
  30. package/dashboard/app/components/KnowledgeSourcesList.vue +13 -9
  31. package/dashboard/app/components/MarkdownEditor.vue +1 -1
  32. package/dashboard/app/components/NotificationsBell.vue +8 -4
  33. package/dashboard/app/components/OnboardingTour.vue +24 -11
  34. package/dashboard/app/components/PersonaCloneDialog.vue +15 -8
  35. package/dashboard/app/components/PersonaWizard.vue +105 -72
  36. package/dashboard/app/components/SidebarFavoritesWidget.vue +5 -5
  37. package/dashboard/app/components/SidebarStatsWidget.vue +3 -1
  38. package/dashboard/app/components/Terminal.vue +9 -8
  39. package/dashboard/app/components/TextDiff.vue +27 -15
  40. package/dashboard/app/composables/useActivityFeed.ts +3 -3
  41. package/dashboard/app/composables/useApi.ts +4 -2
  42. package/dashboard/app/composables/useConfirmDialog.ts +1 -1
  43. package/dashboard/app/composables/useFavorites.ts +8 -8
  44. package/dashboard/app/composables/useTerminalThemes.ts +13 -13
  45. package/dashboard/app/composables/useThemeColor.ts +7 -4
  46. package/dashboard/app/pages/agents/[id].vue +319 -159
  47. package/dashboard/app/pages/agents/compare.vue +152 -52
  48. package/dashboard/app/pages/agents/index.vue +68 -55
  49. package/dashboard/app/pages/agents/new.vue +125 -58
  50. package/dashboard/app/pages/audit.vue +13 -6
  51. package/dashboard/app/pages/budget.vue +20 -18
  52. package/dashboard/app/pages/commands.vue +26 -22
  53. package/dashboard/app/pages/departments/[dept].vue +67 -31
  54. package/dashboard/app/pages/departments/compare.vue +85 -27
  55. package/dashboard/app/pages/departments/index.vue +31 -7
  56. package/dashboard/app/pages/health.vue +19 -12
  57. package/dashboard/app/pages/knowledge/index.vue +190 -78
  58. package/dashboard/app/pages/models.vue +74 -28
  59. package/dashboard/app/pages/personas/[id].vue +322 -219
  60. package/dashboard/app/pages/personas/archetypes.vue +20 -6
  61. package/dashboard/app/pages/personas/compare-with-agent.vue +109 -39
  62. package/dashboard/app/pages/personas/compare.vue +196 -124
  63. package/dashboard/app/pages/personas/index.vue +67 -60
  64. package/dashboard/app/pages/personas/new.vue +1 -1
  65. package/dashboard/app/pages/settings.vue +1 -0
  66. package/dashboard/app/pages/tasks.vue +80 -38
  67. package/dashboard/app/pages/trash.vue +11 -9
  68. package/dashboard/app/pages/workflows.vue +29 -19
  69. package/dashboard/nuxt.config.ts +2 -1
  70. package/dashboard/package.json +5 -0
  71. package/departments/kb/skills/doc-extraction/SKILL.md +99 -0
  72. package/departments/kb/skills/doc-redaction/SKILL.md +103 -0
  73. package/departments/kb/skills/knowledge-ops/SKILL.md +88 -0
  74. package/departments/ops/skills/github-ops/SKILL.md +89 -0
  75. package/departments/ops/skills/terminal-ops/SKILL.md +83 -0
  76. package/installer/index.js +1 -1
  77. package/knowledge/commands-registry.json +16 -3
  78. package/knowledge/skills-manifest.json +66 -1
  79. package/package.json +1 -1
  80. package/pyproject.toml +1 -1
@@ -13,9 +13,11 @@ Section order preserved exactly:
13
13
  [arka:meta], kb-cite, sycophancy, closing marker, skill proposer,
14
14
  kb=N reconciliation) → telemetry entry (mode: warn)
15
15
  6. Gate checkpoint persistence (core/workflow/gate_checkpoint.py)
16
- 7. Auto-documentor enqueue (flow-required + QG APPROVED + external
16
+ 7. Stop-lint batch enqueue (detached scoped lint
17
+ core/governance/stop_lint.py, warn-only telemetry)
18
+ 8. Auto-documentor enqueue (flow-required + QG APPROVED + external
17
19
  research heuristic)
18
- 8. Belt-and-braces WF_MARKER removal
20
+ 9. Belt-and-braces WF_MARKER removal
19
21
 
20
22
  The transcript is read from disk ONCE and shared across native-usage,
21
23
  flow validation, gate checkpoint, and the auto-doc external-research
@@ -24,13 +26,13 @@ heuristic (the old hook parsed it four times).
24
26
 
25
27
  from __future__ import annotations
26
28
 
29
+ import contextlib
27
30
  import json
28
31
  import os
29
32
  import re
30
- from datetime import datetime, timezone
33
+ from datetime import UTC, datetime
31
34
  from pathlib import Path
32
35
 
33
- from core.shared.temp_paths import arkaos_temp_dir
34
36
  from core.hooks._shared import (
35
37
  ensure_root_on_path,
36
38
  get_str,
@@ -39,6 +41,7 @@ from core.hooks._shared import (
39
41
  resolve_arkaos_root,
40
42
  safe_session_id,
41
43
  )
44
+ from core.shared.temp_paths import arkaos_temp_dir
42
45
 
43
46
  _DISPATCH_RE = re.compile(
44
47
  r"\[arka:dispatch\][ \t]*[A-Za-z0-9_-]+[ \t]*->[ \t]*([A-Za-z0-9_-]+)",
@@ -164,8 +167,8 @@ def _flow_checks(
164
167
  from core.governance.phantom_action_check import (
165
168
  current_turn_assistant_texts,
166
169
  )
167
- from core.workflow.gate_checkpoint import extract_latest_gate
168
170
  from core.workflow import plan_approval
171
+ from core.workflow.gate_checkpoint import extract_latest_gate
169
172
  turn = current_turn_assistant_texts(raw)
170
173
  scan = turn if turn is not None else [last]
171
174
  if session_id and extract_latest_gate(scan) == 2:
@@ -300,8 +303,31 @@ def _flow_checks(
300
303
  except Exception:
301
304
  pass
302
305
 
306
+ # Context-monitor tool-loop signal (warn-only): turn-boundary scan
307
+ # over the transcript sees every call, fast-pathed ones included.
308
+ loop_detected = False
309
+ loop_tool = ""
310
+ loop_repeats = 0
311
+ loop_pattern = ""
312
+ try:
313
+ from core.governance.tool_loop_check import check_tool_loops
314
+ lv = check_tool_loops(raw)
315
+ loop_detected = lv.detected
316
+ loop_tool = lv.tool
317
+ loop_repeats = lv.repeats
318
+ loop_pattern = lv.pattern
319
+ if lv.detected and safe_sid:
320
+ _write_tmp_state("arkaos-tool-loop", safe_sid, {
321
+ "tool": lv.tool,
322
+ "repeats": lv.repeats,
323
+ "pattern": lv.pattern,
324
+ "total_tool_uses": lv.total_tool_uses,
325
+ })
326
+ except Exception:
327
+ pass
328
+
303
329
  entry = {
304
- "ts": datetime.now(timezone.utc).isoformat(),
330
+ "ts": datetime.now(UTC).isoformat(),
305
331
  "session_id": session_id,
306
332
  "cwd": cwd,
307
333
  "event": "stop-hook-flow-check",
@@ -326,6 +352,10 @@ def _flow_checks(
326
352
  "phantom_check_passed": phantom_passed,
327
353
  "phantom_check_reason": phantom_reason,
328
354
  "phantom_claims": phantom_claims,
355
+ "tool_loop_detected": loop_detected,
356
+ "tool_loop_tool": loop_tool,
357
+ "tool_loop_repeats": loop_repeats,
358
+ "tool_loop_pattern": loop_pattern,
329
359
  "effort_level": effort_level,
330
360
  "mode": "warn",
331
361
  }
@@ -336,16 +366,14 @@ def _flow_checks(
336
366
  except Exception:
337
367
  pass
338
368
 
339
- try:
369
+ with contextlib.suppress(Exception):
340
370
  clear_flow_required(session_id)
341
- except Exception:
342
- pass
343
371
 
344
372
 
345
373
  def _auto_doc_enqueue(
346
374
  session_id: str, transcript_path: str, raw: str | None
347
375
  ) -> None:
348
- """Section 7: fire-and-forget auto-documentor job enqueue."""
376
+ """Section 8: fire-and-forget auto-documentor job enqueue."""
349
377
  try:
350
378
  from core.jobs.auto_doc_worker import enqueue_job
351
379
  from core.workflow.flow_enforcer import _load_last_assistant_messages
@@ -387,10 +415,8 @@ def _auto_doc_enqueue(
387
415
  data = raw if raw is not None else ""
388
416
  if not any(marker in data for marker in _EXTERNAL_MARKERS):
389
417
  return
390
- try:
418
+ with contextlib.suppress(Exception):
391
419
  enqueue_job(session_id, transcript_path, "APPROVED")
392
- except Exception:
393
- pass
394
420
 
395
421
 
396
422
  def _session_memory_enabled() -> bool:
@@ -426,7 +452,33 @@ def _enqueue_turn_capture(session_id: str, transcript_path: str, cwd: str) -> No
426
452
  env={**os.environ, "PYTHONPATH": repo},
427
453
  start_new_session=True, # same pattern as _enqueue_cognition_capture
428
454
  )
429
- except Exception: # noqa: BLE001 — capture is best-effort, hook never breaks
455
+ except Exception:
456
+ pass
457
+
458
+
459
+ def _enqueue_stop_lint(session_id: str, cwd: str) -> None:
460
+ """Section 7: detached scoped lint batch — deterministic evidence
461
+ lands in telemetry (stop-lint.jsonl), never on the 5s hook budget."""
462
+ if not cwd or not Path(cwd).is_dir():
463
+ return
464
+ try:
465
+ from core.governance.stop_lint import mode as stop_lint_mode
466
+ if stop_lint_mode() == "off":
467
+ return
468
+ repo = repo_path()
469
+ if not repo or not Path(repo).is_dir():
470
+ return
471
+ import subprocess
472
+ import sys as _sys
473
+ subprocess.Popen(
474
+ [_sys.executable, "-m", "core.governance.stop_lint",
475
+ cwd, session_id or ""],
476
+ stdout=subprocess.DEVNULL,
477
+ stderr=subprocess.DEVNULL,
478
+ env={**os.environ, "PYTHONPATH": repo},
479
+ start_new_session=True, # same pattern as _enqueue_turn_capture
480
+ )
481
+ except Exception: # best-effort, hook never breaks
430
482
  pass
431
483
 
432
484
 
@@ -450,7 +502,7 @@ def _enqueue_routing_rebuild() -> None:
450
502
  env={**os.environ, "PYTHONPATH": repo},
451
503
  start_new_session=True,
452
504
  )
453
- except Exception: # noqa: BLE001 — rebuild is best-effort, hook never breaks
505
+ except Exception:
454
506
  pass
455
507
 
456
508
 
@@ -479,10 +531,8 @@ def main(stdin_json: dict | None = None) -> int:
479
531
 
480
532
  _native_usage(transcript_path, session_id, raw)
481
533
 
482
- try:
534
+ with contextlib.suppress(Exception):
483
535
  _enqueue_turn_capture(session_id, transcript_path, cwd)
484
- except Exception: # noqa: BLE001 — never let capture break the hook
485
- pass
486
536
 
487
537
  _enqueue_routing_rebuild()
488
538
 
@@ -494,13 +544,13 @@ def main(stdin_json: dict | None = None) -> int:
494
544
  if (Path(root) / "core" / "workflow" / "flow_enforcer.py").is_file():
495
545
  _flow_checks(session_id, transcript_path, cwd, effort_level, raw)
496
546
 
547
+ _enqueue_stop_lint(session_id, cwd)
548
+
497
549
  _auto_doc_enqueue(session_id, transcript_path, raw)
498
550
 
499
551
  # Belt-and-braces marker removal (session id already allowlisted).
500
- try:
552
+ with contextlib.suppress(OSError):
501
553
  wf_marker.unlink(missing_ok=True)
502
- except OSError:
503
- pass
504
554
  return 0
505
555
 
506
556
 
@@ -510,4 +560,5 @@ if __name__ == "__main__":
510
560
  except SystemExit:
511
561
  raise
512
562
  except Exception:
513
- raise SystemExit(0) # Stop hook must never break the turn
563
+ # Stop hook must never break the turn.
564
+ raise SystemExit(0) from None
@@ -6,25 +6,59 @@ their public per-token pricing. It is used to compute an optional
6
6
  any model-selection logic — that is explicitly forbidden by the
7
7
  LLM-agnostic contract.
8
8
 
9
- TODO(pricing-snapshot 2026-04-20): values sourced from the public
10
- Anthropic (https://www.anthropic.com/pricing) and OpenAI
11
- (https://openai.com/api/pricing) pricing pages. Refresh when model
12
- families change. Unknown models return `None` from `estimate_cost_usd`
13
- and are logged with a null cost — never a guessed number.
9
+ Snapshot 2026-07-18: values sourced from the public Anthropic pricing
10
+ page (https://platform.claude.com/docs/en/about-claude/pricing) and
11
+ OpenAI (https://openai.com/api/pricing). Refresh when model families
12
+ change. Unknown models return `None` from `estimate_cost_usd` and are
13
+ logged with a null cost — never a guessed number.
14
14
  """
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
18
 
19
19
  # USD per 1M tokens. Only the `cache_read` and `cache_write` keys apply
20
- # to providers that expose prompt caching (currently Anthropic). Missing
21
- # keys fall back to 0 cost contribution rather than raising.
20
+ # to providers that expose prompt caching (currently Anthropic; rates
21
+ # are the 0.1x / 1.25x input multipliers for 5-minute cache writes).
22
+ # Missing keys fall back to 0 cost contribution rather than raising.
22
23
  PRICING: dict[str, dict[str, float]] = {
24
+ "claude-fable-5": {
25
+ "input": 10.00,
26
+ "output": 50.00,
27
+ "cache_read": 1.00,
28
+ "cache_write": 12.50,
29
+ },
30
+ "claude-opus-4-8": {
31
+ "input": 5.00,
32
+ "output": 25.00,
33
+ "cache_read": 0.50,
34
+ "cache_write": 6.25,
35
+ },
23
36
  "claude-opus-4-7": {
24
- "input": 15.00,
25
- "output": 75.00,
26
- "cache_read": 1.50,
27
- "cache_write": 18.75,
37
+ "input": 5.00,
38
+ "output": 25.00,
39
+ "cache_read": 0.50,
40
+ "cache_write": 6.25,
41
+ },
42
+ "claude-opus-4-6": {
43
+ "input": 5.00,
44
+ "output": 25.00,
45
+ "cache_read": 0.50,
46
+ "cache_write": 6.25,
47
+ },
48
+ "claude-opus-4-5": {
49
+ "input": 5.00,
50
+ "output": 25.00,
51
+ "cache_read": 0.50,
52
+ "cache_write": 6.25,
53
+ },
54
+ # Introductory pricing ($2/$10) is in effect through 2026-08-31;
55
+ # from 2026-09-01 the standard rate is $3/$15 (cache $0.30/$3.75).
56
+ # Refresh this row when the introductory window closes.
57
+ "claude-sonnet-5": {
58
+ "input": 2.00,
59
+ "output": 10.00,
60
+ "cache_read": 0.20,
61
+ "cache_write": 2.50,
28
62
  },
29
63
  "claude-sonnet-4-6": {
30
64
  "input": 3.00,
@@ -33,19 +67,19 @@ PRICING: dict[str, dict[str, float]] = {
33
67
  "cache_write": 3.75,
34
68
  },
35
69
  "claude-haiku-4-5-20251001": {
36
- "input": 0.80,
37
- "output": 4.00,
38
- "cache_read": 0.08,
39
- "cache_write": 1.00,
70
+ "input": 1.00,
71
+ "output": 5.00,
72
+ "cache_read": 0.10,
73
+ "cache_write": 1.25,
40
74
  },
41
75
  # Generic (undated) alias of the dated haiku row above — native
42
76
  # transcripts sometimes report the tier id without the date suffix.
43
77
  # Same published price; NOT an invented number.
44
78
  "claude-haiku-4-5": {
45
- "input": 0.80,
46
- "output": 4.00,
47
- "cache_read": 0.08,
48
- "cache_write": 1.00,
79
+ "input": 1.00,
80
+ "output": 5.00,
81
+ "cache_read": 0.10,
82
+ "cache_write": 1.25,
49
83
  },
50
84
  "gpt-4": {
51
85
  "input": 30.00,