@smilintux/skcapstone 0.10.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 +123 -30
- package/docs/DREAMING.md +70 -0
- package/docs/GETTING_STARTED.md +7 -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.skcomm-heartbeat.plist → com.skcapstone.skcomms-heartbeat.plist} +4 -4
- package/launchd/{com.skcapstone.skcomm-queue-drain.plist → com.skcapstone.skcomms-queue-drain.plist} +4 -4
- package/launchd/install-launchd.sh +6 -6
- 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 +7 -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 +159 -5
- package/scripts/model-fallback-monitor.sh +102 -0
- package/scripts/nvidia-proxy.mjs +78 -26
- 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 +3 -3
- package/scripts/telegram-catchup-all.sh +12 -1
- 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/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 +11 -7
- 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 +132 -77
- 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 +14 -12
- package/src/skcapstone/gui_installer.py +2 -2
- package/src/skcapstone/heartbeat.py +1 -1
- 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 +19 -19
- package/src/skcapstone/mcp_launcher.py +15 -1
- package/src/skcapstone/mcp_server.py +83 -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 +875 -121
- 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 +55 -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
|
@@ -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],
|