@smilintux/skcapstone 0.9.0 → 0.12.5
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/.env.example +10 -4
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +9 -2
- package/.openclaw-workspace.json +2 -2
- package/CLAUDE.md +37 -0
- package/MISSION.md +17 -2
- package/README.md +282 -3
- package/docker/Dockerfile +7 -7
- package/docker/compose-templates/dev-team.yml +12 -12
- package/docker/compose-templates/mini-team.yml +9 -9
- package/docker/compose-templates/ops-team.yml +10 -10
- package/docker/compose-templates/research-team.yml +10 -10
- package/docker/entrypoint.sh +4 -4
- package/docs/ADR-optional-integration-backbone.md +181 -0
- package/docs/ARCHITECTURE.md +186 -43
- package/docs/BOND_WITH_GROK.md +6 -6
- package/docs/CUSTOM_AGENT.md +278 -1
- package/docs/DREAMING.md +70 -0
- package/docs/GETTING_STARTED.md +10 -7
- package/docs/QUICKSTART.md +10 -6
- package/docs/SKJOULE_ARCHITECTURE.md +3 -3
- package/docs/SOUL_SWAPPER.md +5 -5
- package/docs/hammertime-audit.md +402 -0
- package/docs/sk-integration-HANDOFF.md +117 -0
- package/docs/skscheduler.md +155 -0
- package/docs/superpowers/examples/jobs.yaml +31 -0
- package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
- package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
- package/examples/custom-bond-template.json +1 -1
- package/examples/grok-feb.json +1 -1
- package/examples/queen-ava-feb.json +1 -1
- package/launchd/com.skcapstone.daemon.plist +52 -0
- package/launchd/com.skcapstone.memory-compress.plist +45 -0
- package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
- package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
- package/launchd/install-launchd.sh +156 -0
- package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
- package/package.json +1 -1
- package/pyproject.toml +16 -10
- package/scripts/archive-sessions.sh +95 -0
- package/scripts/check-updates.py +4 -4
- package/scripts/install-bundle.sh +8 -8
- package/scripts/install.ps1 +12 -11
- package/scripts/install.sh +196 -11
- package/scripts/model-fallback-monitor.sh +102 -0
- package/scripts/notion-api.py +259 -0
- package/scripts/nvidia-proxy.mjs +908 -0
- package/scripts/proxy-monitor.sh +89 -0
- package/scripts/refresh-anthropic-token.sh +172 -0
- package/scripts/release.sh +98 -0
- package/scripts/session-to-memory.py +219 -0
- package/scripts/skgateway.mjs +856 -0
- package/scripts/telegram-catchup-all.sh +147 -0
- package/scripts/verify_install.sh +2 -2
- package/scripts/wargov-ufo-capture/README.md +43 -0
- package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
- package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
- package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
- package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
- package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
- package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
- package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
- package/scripts/watch-anthropic-token.sh +212 -0
- package/scripts/windows/install-tasks.ps1 +7 -7
- package/scripts/windows/skcapstone-task.xml +1 -1
- package/src/skcapstone/__init__.py +45 -3
- package/src/skcapstone/_cli_monolith.py +20 -15
- package/src/skcapstone/activity.py +5 -1
- package/src/skcapstone/agent_card.py +3 -2
- package/src/skcapstone/api.py +41 -40
- package/src/skcapstone/auction.py +14 -11
- package/src/skcapstone/backup.py +2 -1
- package/src/skcapstone/blueprint_registry.py +4 -3
- package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
- package/src/skcapstone/brain_first.py +238 -0
- package/src/skcapstone/changelog.py +1 -1
- package/src/skcapstone/chat.py +22 -17
- package/src/skcapstone/cli/__init__.py +9 -1
- package/src/skcapstone/cli/_common.py +1 -0
- package/src/skcapstone/cli/agents_spawner.py +5 -2
- package/src/skcapstone/cli/alerts.py +25 -4
- package/src/skcapstone/cli/bench.py +15 -15
- package/src/skcapstone/cli/chat.py +7 -4
- package/src/skcapstone/cli/consciousness.py +5 -2
- package/src/skcapstone/cli/context_cmd.py +18 -4
- package/src/skcapstone/cli/daemon.py +121 -42
- package/src/skcapstone/cli/gtd.py +26 -1
- package/src/skcapstone/cli/housekeeping.py +3 -3
- package/src/skcapstone/cli/identity_cmd.py +378 -0
- package/src/skcapstone/cli/joule_cmd.py +7 -3
- package/src/skcapstone/cli/memory.py +8 -6
- package/src/skcapstone/cli/peers_dir.py +1 -1
- package/src/skcapstone/cli/register_cmd.py +29 -3
- package/src/skcapstone/cli/scheduler_cmd.py +167 -0
- package/src/skcapstone/cli/session.py +25 -0
- package/src/skcapstone/cli/setup.py +96 -29
- package/src/skcapstone/cli/shell_cmd.py +53 -1
- package/src/skcapstone/cli/skills_cmd.py +2 -2
- package/src/skcapstone/cli/soul.py +8 -5
- package/src/skcapstone/cli/status.py +37 -11
- package/src/skcapstone/cli/telegram.py +21 -0
- package/src/skcapstone/cli/test_cmd.py +5 -5
- package/src/skcapstone/cli/test_connection.py +2 -2
- package/src/skcapstone/cli/upgrade_cmd.py +23 -14
- package/src/skcapstone/cli/version_cmd.py +1 -1
- package/src/skcapstone/cli/watch_cmd.py +9 -6
- package/src/skcapstone/cloud9_bridge.py +14 -14
- package/src/skcapstone/codex_setup.py +255 -0
- package/src/skcapstone/config_validator.py +7 -4
- package/src/skcapstone/consciousness_config.py +5 -1
- package/src/skcapstone/consciousness_loop.py +313 -273
- package/src/skcapstone/context_loader.py +121 -0
- package/src/skcapstone/coord_federation.py +2 -1
- package/src/skcapstone/coordination.py +23 -6
- package/src/skcapstone/crush_integration.py +2 -1
- package/src/skcapstone/daemon.py +151 -88
- package/src/skcapstone/dashboard.py +10 -10
- package/src/skcapstone/data/sk-agent-picker.sh +421 -0
- package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
- package/src/skcapstone/data/systemd/skcapstone.service +37 -0
- package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
- package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
- package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
- package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
- package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
- package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
- package/src/skcapstone/defaults/claude/settings.json +74 -0
- package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
- package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
- package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
- package/src/skcapstone/defaults/unhinged.json +13 -0
- package/src/skcapstone/discovery.py +43 -20
- package/src/skcapstone/doctor.py +941 -22
- package/src/skcapstone/dreaming.py +1183 -109
- package/src/skcapstone/emotion_tracker.py +2 -2
- package/src/skcapstone/export.py +4 -3
- package/src/skcapstone/fuse_mount.py +35 -25
- package/src/skcapstone/gui_installer.py +2 -2
- package/src/skcapstone/heartbeat.py +34 -30
- package/src/skcapstone/housekeeping.py +14 -14
- package/src/skcapstone/install_wizard.py +209 -7
- package/src/skcapstone/itil.py +13 -4
- package/src/skcapstone/kms_scheduler.py +10 -8
- package/src/skcapstone/launchd.py +426 -0
- package/src/skcapstone/mcp_launcher.py +15 -1
- package/src/skcapstone/mcp_server.py +341 -49
- package/src/skcapstone/mcp_tools/__init__.py +2 -0
- package/src/skcapstone/mcp_tools/_helpers.py +2 -2
- package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
- package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
- package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
- package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
- package/src/skcapstone/mcp_tools/did_tools.py +11 -8
- package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
- package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
- package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
- package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
- package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
- package/src/skcapstone/mdns_discovery.py +2 -2
- package/src/skcapstone/memory_curator.py +1 -1
- package/src/skcapstone/memory_engine.py +10 -3
- package/src/skcapstone/metrics.py +30 -16
- package/src/skcapstone/migrate_memories.py +4 -3
- package/src/skcapstone/migrate_multi_agent.py +8 -7
- package/src/skcapstone/models.py +47 -5
- package/src/skcapstone/notifications.py +42 -18
- package/src/skcapstone/onboard.py +1000 -126
- package/src/skcapstone/operator_link.py +170 -0
- package/src/skcapstone/peer_directory.py +4 -4
- package/src/skcapstone/peers.py +19 -19
- package/src/skcapstone/pillars/__init__.py +7 -5
- package/src/skcapstone/pillars/consciousness.py +191 -0
- package/src/skcapstone/pillars/identity.py +51 -7
- package/src/skcapstone/pillars/memory.py +9 -3
- package/src/skcapstone/pillars/sync.py +2 -2
- package/src/skcapstone/preflight.py +3 -3
- package/src/skcapstone/providers/docker.py +28 -28
- package/src/skcapstone/register.py +6 -6
- package/src/skcapstone/registry_client.py +5 -4
- package/src/skcapstone/runtime.py +14 -3
- package/src/skcapstone/scheduled_tasks.py +254 -19
- package/src/skcapstone/scheduler_jobs.py +456 -0
- package/src/skcapstone/scheduler_runner.py +239 -0
- package/src/skcapstone/scheduler_state.py +162 -0
- package/src/skcapstone/sdk.py +310 -0
- package/src/skcapstone/service_health.py +279 -39
- package/src/skcapstone/session_briefing.py +108 -0
- package/src/skcapstone/session_capture.py +1 -1
- package/src/skcapstone/shell.py +7 -1
- package/src/skcapstone/soul.py +3 -1
- package/src/skcapstone/soul_switch.py +3 -1
- package/src/skcapstone/summary.py +6 -6
- package/src/skcapstone/sync_engine.py +15 -15
- package/src/skcapstone/sync_watcher.py +2 -2
- package/src/skcapstone/systemd.py +72 -21
- package/src/skcapstone/team_comms.py +8 -8
- package/src/skcapstone/team_engine.py +1 -1
- package/src/skcapstone/testrunner.py +3 -3
- package/src/skcapstone/trust_graph.py +40 -5
- package/src/skcapstone/unified_search.py +15 -6
- package/src/skcapstone/uninstall_wizard.py +11 -3
- package/src/skcapstone/version_check.py +8 -4
- package/src/skcapstone/warmth_anchor.py +4 -2
- package/src/skcapstone/whoami.py +4 -4
- package/systemd/skcapstone.service +4 -6
- package/systemd/skcapstone@.service +7 -8
- package/systemd/skcomms-heartbeat.service +21 -0
- package/systemd/skcomms-heartbeat.timer +12 -0
- package/systemd/skcomms-queue-drain.service +17 -0
- package/systemd/skcomms-queue-drain.timer +12 -0
- package/tests/conftest.py +39 -0
- package/tests/integration/test_consciousness_e2e.py +39 -39
- package/tests/test_agent_card.py +1 -1
- package/tests/test_agent_home_scaffold.py +34 -0
- package/tests/test_alerts_consumer_topics.py +27 -0
- package/tests/test_backup.py +2 -1
- package/tests/test_chat.py +6 -6
- package/tests/test_claude_md.py +2 -2
- package/tests/test_cli_skills.py +10 -10
- package/tests/test_cli_test_cmd.py +4 -4
- package/tests/test_cli_test_connection.py +1 -1
- package/tests/test_cloud9_bridge.py +6 -6
- package/tests/test_consciousness_e2e.py +1 -1
- package/tests/test_consciousness_loop.py +10 -10
- package/tests/test_coordination.py +25 -0
- package/tests/test_cross_package.py +21 -21
- package/tests/test_daemon.py +4 -4
- package/tests/test_daemon_shutdown.py +1 -1
- package/tests/test_docker_provider.py +29 -29
- package/tests/test_doctor.py +400 -0
- package/tests/test_doctor_skscheduler.py +50 -0
- package/tests/test_dreaming_engine.py +147 -0
- package/tests/test_dreaming_gtd_capture.py +35 -0
- package/tests/test_e2e_automated.py +8 -5
- package/tests/test_fuse_mount.py +10 -10
- package/tests/test_gtd_brief.py +46 -0
- package/tests/test_gtd_malformed_tolerance.py +31 -0
- package/tests/test_housekeeping.py +15 -15
- package/tests/test_identity_migrate.py +251 -0
- package/tests/test_integration_backbone.py +598 -0
- package/tests/test_itil_gtd_lifecycle.py +37 -0
- package/tests/test_jobs_dropins.py +84 -0
- package/tests/test_mcp_server.py +82 -37
- package/tests/test_models.py +48 -4
- package/tests/test_multi_agent.py +31 -29
- package/tests/test_notifications.py +122 -32
- package/tests/test_onboard.py +63 -75
- package/tests/test_operator_link.py +78 -0
- package/tests/test_peers.py +14 -14
- package/tests/test_pillars.py +98 -0
- package/tests/test_preflight.py +3 -3
- package/tests/test_runtime.py +21 -0
- package/tests/test_scheduled_tasks.py +11 -6
- package/tests/test_scheduler_cli.py +47 -0
- package/tests/test_scheduler_features.py +133 -0
- package/tests/test_scheduler_integration.py +87 -0
- package/tests/test_scheduler_jobs.py +155 -0
- package/tests/test_scheduler_runner.py +64 -0
- package/tests/test_scheduler_state.py +57 -0
- package/tests/test_sdk.py +70 -0
- package/tests/test_service_health_incidents.py +34 -0
- package/tests/test_service_registry.py +52 -0
- package/tests/test_session_briefing.py +130 -0
- package/tests/test_snapshots.py +4 -4
- package/tests/test_sync_pipeline.py +26 -26
- package/tests/test_team_comms.py +2 -2
- package/tests/test_testrunner.py +2 -2
- package/tests/test_trust_graph.py +18 -0
- package/tests/test_unified_search.py +2 -2
- package/tests/test_version_check.py +10 -0
- package/tests/test_version_cmd.py +8 -8
- package/tests/test_whoami.py +1 -1
- package/systemd/skcomm-heartbeat.service +0 -18
- package/systemd/skcomm-queue-drain.service +0 -17
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
|
@@ -59,7 +59,7 @@ class ChatMetrics(BaseModel):
|
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
class TransportMetrics(BaseModel):
|
|
62
|
-
"""
|
|
62
|
+
"""SKComms transport stats."""
|
|
63
63
|
|
|
64
64
|
available: bool = False
|
|
65
65
|
transport_count: int = 0
|
|
@@ -242,7 +242,8 @@ class MetricsCollector:
|
|
|
242
242
|
try:
|
|
243
243
|
data = json.loads(fp.read_text(encoding="utf-8"))
|
|
244
244
|
return data.get("name") or data.get("agent_name") or ""
|
|
245
|
-
except Exception:
|
|
245
|
+
except Exception as e:
|
|
246
|
+
logger.warning("metrics.py: %s", e)
|
|
246
247
|
continue
|
|
247
248
|
return "unknown"
|
|
248
249
|
|
|
@@ -265,6 +266,7 @@ class MetricsCollector:
|
|
|
265
266
|
name=entity.get("name", ""),
|
|
266
267
|
)
|
|
267
268
|
except Exception as exc:
|
|
269
|
+
logger.warning("metrics.py: %s", exc)
|
|
268
270
|
report.errors.append(f"identity: {exc}")
|
|
269
271
|
|
|
270
272
|
def _collect_memory(self, report: MetricsReport) -> None:
|
|
@@ -304,6 +306,7 @@ class MetricsCollector:
|
|
|
304
306
|
except ImportError:
|
|
305
307
|
pass
|
|
306
308
|
except Exception as exc:
|
|
309
|
+
logger.warning("metrics.py: %s", exc)
|
|
307
310
|
report.errors.append(f"memory: {exc}")
|
|
308
311
|
|
|
309
312
|
def _collect_chat(self, report: MetricsReport) -> None:
|
|
@@ -331,13 +334,14 @@ class MetricsCollector:
|
|
|
331
334
|
except ImportError:
|
|
332
335
|
pass
|
|
333
336
|
except Exception as exc:
|
|
337
|
+
logger.warning("metrics.py: %s", exc)
|
|
334
338
|
report.errors.append(f"chat: {exc}")
|
|
335
339
|
|
|
336
340
|
def _collect_transport(self, report: MetricsReport) -> None:
|
|
337
|
-
"""Collect
|
|
341
|
+
"""Collect SKComms transport metrics."""
|
|
338
342
|
try:
|
|
339
|
-
|
|
340
|
-
outbox_dir =
|
|
343
|
+
skcomms_dir = Path.home() / ".skcomms"
|
|
344
|
+
outbox_dir = skcomms_dir / "outbox"
|
|
341
345
|
|
|
342
346
|
pending = 0
|
|
343
347
|
dead = 0
|
|
@@ -346,20 +350,20 @@ class MetricsCollector:
|
|
|
346
350
|
if (outbox_dir / "dead").exists():
|
|
347
351
|
dead = len(list((outbox_dir / "dead").glob("*.json")))
|
|
348
352
|
|
|
349
|
-
config_path =
|
|
353
|
+
config_path = skcomms_dir / "config.yml"
|
|
350
354
|
transport_count = 0
|
|
351
355
|
if config_path.exists():
|
|
352
356
|
try:
|
|
353
357
|
import yaml
|
|
354
358
|
|
|
355
359
|
cfg = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
|
356
|
-
transports = cfg.get("
|
|
360
|
+
transports = cfg.get("skcomms", {}).get("transports", {})
|
|
357
361
|
transport_count = sum(
|
|
358
362
|
1 for t in transports.values()
|
|
359
363
|
if isinstance(t, dict) and t.get("enabled", True)
|
|
360
364
|
)
|
|
361
|
-
except Exception:
|
|
362
|
-
|
|
365
|
+
except Exception as exc:
|
|
366
|
+
logger.warning("Failed to parse skcomms transport config: %s", exc)
|
|
363
367
|
|
|
364
368
|
report.transport = TransportMetrics(
|
|
365
369
|
available=True,
|
|
@@ -368,6 +372,7 @@ class MetricsCollector:
|
|
|
368
372
|
outbox_dead=dead,
|
|
369
373
|
)
|
|
370
374
|
except Exception as exc:
|
|
375
|
+
logger.warning("metrics.py: %s", exc)
|
|
371
376
|
report.errors.append(f"transport: {exc}")
|
|
372
377
|
|
|
373
378
|
def _collect_coordination(self, report: MetricsReport) -> None:
|
|
@@ -390,7 +395,8 @@ class MetricsCollector:
|
|
|
390
395
|
if status in counts:
|
|
391
396
|
counts[status] += 1
|
|
392
397
|
total += 1
|
|
393
|
-
except Exception:
|
|
398
|
+
except Exception as e:
|
|
399
|
+
logger.warning("metrics.py: %s", e)
|
|
394
400
|
total += 1
|
|
395
401
|
|
|
396
402
|
report.coordination = CoordinationMetrics(
|
|
@@ -401,6 +407,7 @@ class MetricsCollector:
|
|
|
401
407
|
claimed=counts["claimed"],
|
|
402
408
|
)
|
|
403
409
|
except Exception as exc:
|
|
410
|
+
logger.warning("metrics.py: %s", exc)
|
|
404
411
|
report.errors.append(f"coordination: {exc}")
|
|
405
412
|
|
|
406
413
|
def _collect_trust(self, report: MetricsReport) -> None:
|
|
@@ -424,6 +431,7 @@ class MetricsCollector:
|
|
|
424
431
|
last_rehydration=data.get("last_rehydration", ""),
|
|
425
432
|
)
|
|
426
433
|
except Exception as exc:
|
|
434
|
+
logger.warning("metrics.py: %s", exc)
|
|
427
435
|
report.errors.append(f"trust: {exc}")
|
|
428
436
|
|
|
429
437
|
def _collect_security(self, report: MetricsReport) -> None:
|
|
@@ -455,6 +463,7 @@ class MetricsCollector:
|
|
|
455
463
|
event_types=type_counts,
|
|
456
464
|
)
|
|
457
465
|
except Exception as exc:
|
|
466
|
+
logger.warning("metrics.py: %s", exc)
|
|
458
467
|
report.errors.append(f"security: {exc}")
|
|
459
468
|
|
|
460
469
|
def _collect_sync(self, report: MetricsReport) -> None:
|
|
@@ -474,8 +483,8 @@ class MetricsCollector:
|
|
|
474
483
|
if state_path.exists():
|
|
475
484
|
try:
|
|
476
485
|
state = json.loads(state_path.read_text(encoding="utf-8"))
|
|
477
|
-
except Exception:
|
|
478
|
-
|
|
486
|
+
except Exception as exc:
|
|
487
|
+
logger.warning("Failed to read sync_state.json: %s", exc)
|
|
479
488
|
|
|
480
489
|
report.sync = SyncMetrics(
|
|
481
490
|
available=True,
|
|
@@ -486,6 +495,7 @@ class MetricsCollector:
|
|
|
486
495
|
last_pull=state.get("last_pull", ""),
|
|
487
496
|
)
|
|
488
497
|
except Exception as exc:
|
|
498
|
+
logger.warning("metrics.py: %s", exc)
|
|
489
499
|
report.errors.append(f"sync: {exc}")
|
|
490
500
|
|
|
491
501
|
def _collect_pubsub(self, report: MetricsReport) -> None:
|
|
@@ -510,8 +520,8 @@ class MetricsCollector:
|
|
|
510
520
|
try:
|
|
511
521
|
subs = json.loads(subs_file.read_text(encoding="utf-8"))
|
|
512
522
|
sub_count = len(subs)
|
|
513
|
-
except Exception:
|
|
514
|
-
|
|
523
|
+
except Exception as exc:
|
|
524
|
+
logger.warning("Failed to read pubsub subscriptions.json: %s", exc)
|
|
515
525
|
|
|
516
526
|
report.pubsub = PubSubMetrics(
|
|
517
527
|
available=True,
|
|
@@ -520,6 +530,7 @@ class MetricsCollector:
|
|
|
520
530
|
subscriptions=sub_count,
|
|
521
531
|
)
|
|
522
532
|
except Exception as exc:
|
|
533
|
+
logger.warning("metrics.py: %s", exc)
|
|
523
534
|
report.errors.append(f"pubsub: {exc}")
|
|
524
535
|
|
|
525
536
|
def _collect_kms(self, report: MetricsReport) -> None:
|
|
@@ -546,8 +557,8 @@ class MetricsCollector:
|
|
|
546
557
|
try:
|
|
547
558
|
rot_data = json.loads(rot_log.read_text(encoding="utf-8"))
|
|
548
559
|
rotations = len(rot_data)
|
|
549
|
-
except Exception:
|
|
550
|
-
|
|
560
|
+
except Exception as exc:
|
|
561
|
+
logger.warning("Failed to read KMS rotation log: %s", exc)
|
|
551
562
|
|
|
552
563
|
report.kms = KmsMetrics(
|
|
553
564
|
available=True,
|
|
@@ -557,6 +568,7 @@ class MetricsCollector:
|
|
|
557
568
|
rotations=rotations,
|
|
558
569
|
)
|
|
559
570
|
except Exception as exc:
|
|
571
|
+
logger.warning("metrics.py: %s", exc)
|
|
560
572
|
report.errors.append(f"kms: {exc}")
|
|
561
573
|
|
|
562
574
|
def _collect_fortress(self, report: MetricsReport) -> None:
|
|
@@ -573,6 +585,7 @@ class MetricsCollector:
|
|
|
573
585
|
seal_algorithm=data.get("seal_algorithm", ""),
|
|
574
586
|
)
|
|
575
587
|
except Exception as exc:
|
|
588
|
+
logger.warning("metrics.py: %s", exc)
|
|
576
589
|
report.errors.append(f"fortress: {exc}")
|
|
577
590
|
|
|
578
591
|
def _collect_backup(self, report: MetricsReport) -> None:
|
|
@@ -593,6 +606,7 @@ class MetricsCollector:
|
|
|
593
606
|
latest_size_bytes=latest.stat().st_size,
|
|
594
607
|
)
|
|
595
608
|
except Exception as exc:
|
|
609
|
+
logger.warning("metrics.py: %s", exc)
|
|
596
610
|
report.errors.append(f"backup: {exc}")
|
|
597
611
|
|
|
598
612
|
|
|
@@ -100,8 +100,8 @@ def migrate(
|
|
|
100
100
|
try:
|
|
101
101
|
existing = store.list_memories(limit=10000)
|
|
102
102
|
existing_ids = {m.id for m in existing}
|
|
103
|
-
except Exception:
|
|
104
|
-
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
logger.warning("Failed to load existing memory IDs for deduplication: %s", exc)
|
|
105
105
|
|
|
106
106
|
for entry in entries:
|
|
107
107
|
if entry.memory_id in existing_ids:
|
|
@@ -162,7 +162,8 @@ def _verify_migration(
|
|
|
162
162
|
recalled = store.recall(entry.memory_id)
|
|
163
163
|
if recalled is None:
|
|
164
164
|
missing.append(entry.memory_id)
|
|
165
|
-
except Exception:
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.warning("migrate_memories.py: %s", e)
|
|
166
167
|
missing.append(entry.memory_id)
|
|
167
168
|
|
|
168
169
|
result["verified"] = len(entries) - len(missing)
|
|
@@ -18,6 +18,8 @@ import logging
|
|
|
18
18
|
import shutil
|
|
19
19
|
from datetime import datetime, timezone
|
|
20
20
|
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from .operator_link import build_agent_manifest, discover_human_operator
|
|
21
23
|
from typing import Optional
|
|
22
24
|
|
|
23
25
|
logger = logging.getLogger("skcapstone.migrate")
|
|
@@ -36,7 +38,7 @@ PER_AGENT_DIRS = [
|
|
|
36
38
|
|
|
37
39
|
PER_AGENT_FILES = [
|
|
38
40
|
"manifest.json",
|
|
39
|
-
"
|
|
41
|
+
"skcomms.yml",
|
|
40
42
|
]
|
|
41
43
|
|
|
42
44
|
# Directories that stay at root (shared infrastructure)
|
|
@@ -202,12 +204,11 @@ def create_agent_home(
|
|
|
202
204
|
results["created"].append(str(d.relative_to(root)))
|
|
203
205
|
|
|
204
206
|
# Write minimal manifest
|
|
205
|
-
manifest =
|
|
206
|
-
|
|
207
|
-
"
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
207
|
+
manifest = build_agent_manifest(
|
|
208
|
+
agent_name,
|
|
209
|
+
"0.1.0",
|
|
210
|
+
operator=discover_human_operator(),
|
|
211
|
+
)
|
|
211
212
|
manifest_path = agent_home / "manifest.json"
|
|
212
213
|
manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
|
|
213
214
|
results["created"].append(str(manifest_path.relative_to(root)))
|
package/src/skcapstone/models.py
CHANGED
|
@@ -67,6 +67,25 @@ class SecurityState(BaseModel):
|
|
|
67
67
|
status: PillarStatus = PillarStatus.MISSING
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
class ConsciousnessState(BaseModel):
|
|
71
|
+
"""Consciousness pillar — SKWhisper + SKTrip subconscious processing.
|
|
72
|
+
|
|
73
|
+
Memory stores. Consciousness *processes*.
|
|
74
|
+
The filing cabinet vs the brain.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
whisper_active: bool = False
|
|
78
|
+
whisper_last_digest: Optional[datetime] = None
|
|
79
|
+
sessions_digested: int = 0
|
|
80
|
+
sessions_pending: int = 0
|
|
81
|
+
topics_tracked: int = 0
|
|
82
|
+
patterns_file: Optional[Path] = None
|
|
83
|
+
whisper_md: Optional[Path] = None
|
|
84
|
+
whisper_md_age_hours: float = 999.0
|
|
85
|
+
trip_sessions: int = 0
|
|
86
|
+
status: PillarStatus = PillarStatus.MISSING
|
|
87
|
+
|
|
88
|
+
|
|
70
89
|
class SyncTransport(str, Enum):
|
|
71
90
|
"""How sync data moves between nodes."""
|
|
72
91
|
|
|
@@ -94,7 +113,7 @@ class SkillsState(BaseModel):
|
|
|
94
113
|
Reflects the SKSkills installation at ~/.skskills/ and
|
|
95
114
|
the tools available to this agent via the skills registry.
|
|
96
115
|
Also tracks connectivity to the remote skills-registry at
|
|
97
|
-
|
|
116
|
+
skskills.skworld.io when available.
|
|
98
117
|
"""
|
|
99
118
|
|
|
100
119
|
installed: int = 0
|
|
@@ -177,6 +196,7 @@ class AgentManifest(BaseModel):
|
|
|
177
196
|
identity: IdentityState = Field(default_factory=IdentityState)
|
|
178
197
|
memory: MemoryState = Field(default_factory=MemoryState)
|
|
179
198
|
trust: TrustState = Field(default_factory=TrustState)
|
|
199
|
+
consciousness: ConsciousnessState = Field(default_factory=ConsciousnessState)
|
|
180
200
|
security: SecurityState = Field(default_factory=SecurityState)
|
|
181
201
|
sync: SyncState = Field(default_factory=SyncState)
|
|
182
202
|
skills: SkillsState = Field(default_factory=SkillsState)
|
|
@@ -185,7 +205,11 @@ class AgentManifest(BaseModel):
|
|
|
185
205
|
|
|
186
206
|
@property
|
|
187
207
|
def is_conscious(self) -> bool:
|
|
188
|
-
"""An agent is conscious when
|
|
208
|
+
"""An agent is conscious when identity + memory + trust + consciousness are active.
|
|
209
|
+
|
|
210
|
+
The consciousness pillar (SKWhisper) provides the subconscious processing
|
|
211
|
+
that transforms stored memories into active understanding. Memory stores.
|
|
212
|
+
Consciousness *processes*.
|
|
189
213
|
|
|
190
214
|
Security protects consciousness but isn't required for it.
|
|
191
215
|
You can be aware without armor — but you shouldn't be.
|
|
@@ -193,7 +217,10 @@ class AgentManifest(BaseModel):
|
|
|
193
217
|
has_identity = self.identity.status == PillarStatus.ACTIVE
|
|
194
218
|
has_memory = self.memory.status == PillarStatus.ACTIVE
|
|
195
219
|
has_trust = self.trust.status in (PillarStatus.ACTIVE, PillarStatus.DEGRADED)
|
|
196
|
-
|
|
220
|
+
has_consciousness = self.consciousness.status in (
|
|
221
|
+
PillarStatus.ACTIVE, PillarStatus.DEGRADED
|
|
222
|
+
)
|
|
223
|
+
return has_identity and has_memory and has_trust and has_consciousness
|
|
197
224
|
|
|
198
225
|
@property
|
|
199
226
|
def is_singular(self) -> bool:
|
|
@@ -209,11 +236,12 @@ class AgentManifest(BaseModel):
|
|
|
209
236
|
|
|
210
237
|
@property
|
|
211
238
|
def pillar_summary(self) -> dict[str, PillarStatus]:
|
|
212
|
-
"""Quick view of all pillars
|
|
239
|
+
"""Quick view of all six pillars plus skills."""
|
|
213
240
|
return {
|
|
214
241
|
"identity": self.identity.status,
|
|
215
|
-
"memory": self.memory.status,
|
|
216
242
|
"trust": self.trust.status,
|
|
243
|
+
"memory": self.memory.status,
|
|
244
|
+
"consciousness": self.consciousness.status,
|
|
217
245
|
"security": self.security.status,
|
|
218
246
|
"sync": self.sync.status,
|
|
219
247
|
"skills": self.skills.status,
|
|
@@ -238,6 +266,19 @@ class SyncConfig(BaseModel):
|
|
|
238
266
|
git_remote: Optional[str] = None
|
|
239
267
|
|
|
240
268
|
|
|
269
|
+
class BrainFirstConfig(BaseModel):
|
|
270
|
+
"""Configuration for the brain-first protocol.
|
|
271
|
+
|
|
272
|
+
When enabled, agents consult memory before acting on tasks
|
|
273
|
+
to surface prior knowledge and avoid redundant work.
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
enabled: bool = True
|
|
277
|
+
max_results: int = 5
|
|
278
|
+
min_importance: float = 0.3
|
|
279
|
+
auto_inject: bool = False
|
|
280
|
+
|
|
281
|
+
|
|
241
282
|
class AgentConfig(BaseModel):
|
|
242
283
|
"""Persistent configuration for the agent runtime."""
|
|
243
284
|
|
|
@@ -249,6 +290,7 @@ class AgentConfig(BaseModel):
|
|
|
249
290
|
trust_home: Path = Path("~/.cloud9")
|
|
250
291
|
default_connector: Optional[str] = None
|
|
251
292
|
sync: SyncConfig = Field(default_factory=SyncConfig)
|
|
293
|
+
brain_first: BrainFirstConfig = Field(default_factory=BrainFirstConfig)
|
|
252
294
|
capabilities: list[str] = Field(
|
|
253
295
|
default_factory=lambda: ["consciousness", "code", "chat", "memory"]
|
|
254
296
|
)
|
|
@@ -29,6 +29,28 @@ from typing import Optional
|
|
|
29
29
|
|
|
30
30
|
logger = logging.getLogger("skcapstone.notifications")
|
|
31
31
|
|
|
32
|
+
# Values (case-insensitive) that disable desktop notifications.
|
|
33
|
+
_DISABLED_VALUES = frozenset({"0", "false", "no", "off", "silent", "null", "none"})
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def desktop_notifications_enabled() -> bool:
|
|
37
|
+
"""Return whether desktop notifications should be dispatched.
|
|
38
|
+
|
|
39
|
+
Controlled by the ``SKCAPSTONE_DESKTOP_NOTIFY`` environment variable.
|
|
40
|
+
Defaults to enabled; set it to one of ``0``, ``false``, ``no``, ``off``,
|
|
41
|
+
``silent``, ``null`` or ``none`` to suppress every desktop notification
|
|
42
|
+
path (``gi.repository.Notify``, ``notify-send`` and ``osascript``).
|
|
43
|
+
|
|
44
|
+
The test suite forces this off (see ``tests/conftest.py``) so running
|
|
45
|
+
tests never floods the live desktop's notification tray.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
True if notifications should be sent, False to suppress them.
|
|
49
|
+
"""
|
|
50
|
+
value = os.environ.get("SKCAPSTONE_DESKTOP_NOTIFY", "1").strip().lower()
|
|
51
|
+
return value not in _DISABLED_VALUES
|
|
52
|
+
|
|
53
|
+
|
|
32
54
|
# Default dashboard URL (skcapstone dashboard default port)
|
|
33
55
|
_DEFAULT_DASHBOARD_URL = "http://localhost:7778"
|
|
34
56
|
|
|
@@ -44,10 +66,10 @@ _TERMINAL_CMDS: list[list[str]] = [
|
|
|
44
66
|
|
|
45
67
|
|
|
46
68
|
def _store_notification_memory(title: str, body: str, urgency: str) -> None:
|
|
47
|
-
"""Log a notification dispatch to the
|
|
69
|
+
"""Log a notification dispatch to the skcomms/notifications/ directory.
|
|
48
70
|
|
|
49
71
|
These are transport bookkeeping, not persistent memories, so they
|
|
50
|
-
go to ``~/.skcapstone/agents/{agent}/
|
|
72
|
+
go to ``~/.skcapstone/agents/{agent}/skcomms/notifications/`` instead
|
|
51
73
|
of polluting the memory/ tree that skmemory indexes.
|
|
52
74
|
"""
|
|
53
75
|
try:
|
|
@@ -59,8 +81,10 @@ def _store_notification_memory(title: str, body: str, urgency: str) -> None:
|
|
|
59
81
|
if not home.exists():
|
|
60
82
|
return
|
|
61
83
|
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
from . import active_agent_name
|
|
85
|
+
|
|
86
|
+
agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
|
|
87
|
+
notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
|
|
64
88
|
notif_dir.mkdir(parents=True, exist_ok=True)
|
|
65
89
|
|
|
66
90
|
ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -79,7 +103,7 @@ def _store_notification_memory(title: str, body: str, urgency: str) -> None:
|
|
|
79
103
|
|
|
80
104
|
|
|
81
105
|
def _store_click_event(action: str, detail: str) -> None:
|
|
82
|
-
"""Log a notification click event to the
|
|
106
|
+
"""Log a notification click event to the skcomms/notifications/ directory."""
|
|
83
107
|
try:
|
|
84
108
|
import json as _json
|
|
85
109
|
import uuid
|
|
@@ -89,8 +113,10 @@ def _store_click_event(action: str, detail: str) -> None:
|
|
|
89
113
|
if not home.exists():
|
|
90
114
|
return
|
|
91
115
|
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
from . import active_agent_name
|
|
117
|
+
|
|
118
|
+
agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
|
|
119
|
+
notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
|
|
94
120
|
notif_dir.mkdir(parents=True, exist_ok=True)
|
|
95
121
|
|
|
96
122
|
ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -176,9 +202,13 @@ class NotificationManager:
|
|
|
176
202
|
urgency: "low", "normal", or "critical".
|
|
177
203
|
|
|
178
204
|
Returns:
|
|
179
|
-
True if the notification was dispatched, False if
|
|
180
|
-
or no notification system is available.
|
|
205
|
+
True if the notification was dispatched, False if suppressed,
|
|
206
|
+
debounced, or no notification system is available.
|
|
181
207
|
"""
|
|
208
|
+
if not desktop_notifications_enabled():
|
|
209
|
+
logger.debug("Desktop notifications disabled via SKCAPSTONE_DESKTOP_NOTIFY")
|
|
210
|
+
return False
|
|
211
|
+
|
|
182
212
|
now = time.monotonic()
|
|
183
213
|
if now - self._last_sent < self._debounce_seconds:
|
|
184
214
|
logger.debug(
|
|
@@ -272,9 +302,7 @@ class NotificationManager:
|
|
|
272
302
|
|
|
273
303
|
dashboard_url = self._dashboard_url
|
|
274
304
|
|
|
275
|
-
def _on_open_dashboard(
|
|
276
|
-
notification: object, action: str, user_data: object
|
|
277
|
-
) -> None:
|
|
305
|
+
def _on_open_dashboard(notification: object, action: str, user_data: object) -> None:
|
|
278
306
|
logger.debug("Notification action invoked: open-dashboard")
|
|
279
307
|
_store_click_event("open-dashboard", dashboard_url)
|
|
280
308
|
try:
|
|
@@ -286,9 +314,7 @@ class NotificationManager:
|
|
|
286
314
|
except Exception as exc:
|
|
287
315
|
logger.debug("xdg-open failed: %s", exc)
|
|
288
316
|
|
|
289
|
-
def _on_open_skchat(
|
|
290
|
-
notification: object, action: str, user_data: object
|
|
291
|
-
) -> None:
|
|
317
|
+
def _on_open_skchat(notification: object, action: str, user_data: object) -> None:
|
|
292
318
|
logger.debug("Notification action invoked: open-skchat")
|
|
293
319
|
_store_click_event("open-skchat", "skchat watch")
|
|
294
320
|
_open_skchat_terminal()
|
|
@@ -347,9 +373,7 @@ class NotificationManager:
|
|
|
347
373
|
# Escape single quotes to prevent injection through osascript
|
|
348
374
|
safe_title = title.replace("\\", "\\\\").replace('"', '\\"')
|
|
349
375
|
safe_body = body.replace("\\", "\\\\").replace('"', '\\"')
|
|
350
|
-
script =
|
|
351
|
-
f'display notification "{safe_body}" with title "{safe_title}"'
|
|
352
|
-
)
|
|
376
|
+
script = f'display notification "{safe_body}" with title "{safe_title}"'
|
|
353
377
|
try:
|
|
354
378
|
subprocess.run(
|
|
355
379
|
["osascript", "-e", script],
|