@cohortapp/agent-sdk 2.3.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.
- package/.claude/commands/init-agent.md +104 -0
- package/.claude/commands/init-maestro.md +1187 -0
- package/.claude/settings.json +161 -0
- package/.env.example +216 -0
- package/README.md +632 -0
- package/agents/browser-operator/agent.md +52 -0
- package/agents/calendar-ops/agent.md +50 -0
- package/agents/communications/agent.md +96 -0
- package/agents/decision-log/agent.md +65 -0
- package/agents/desktop-operator/agent.md +59 -0
- package/agents/gmail-operator/agent.md +62 -0
- package/agents/inbound-dispatcher/agent.md +66 -0
- package/agents/inbox-processor/agent.md +39 -0
- package/agents/pmo-execution/agent.md +60 -0
- package/agents/session-spawner/agent.md +64 -0
- package/agents/slack-operator/agent.md +60 -0
- package/agents/whatsapp-operator/agent.md +60 -0
- package/agents/workflow-automation/agent.md +61 -0
- package/archetypes/altitudes/c-suite.yaml +58 -0
- package/archetypes/altitudes/founder.yaml +68 -0
- package/archetypes/altitudes/senior-manager.yaml +63 -0
- package/archetypes/altitudes/svp.yaml +60 -0
- package/archetypes/altitudes/vp.yaml +50 -0
- package/archetypes/archetype.schema.json +77 -0
- package/archetypes/base.yaml +47 -0
- package/archetypes/capabilities/commercial-leader.yaml +159 -0
- package/archetypes/capabilities/compliance-officer.yaml +159 -0
- package/archetypes/capabilities/executive-operator.yaml +169 -0
- package/archetypes/capabilities/finance-leader.yaml +162 -0
- package/archetypes/capabilities/operations-leader.yaml +154 -0
- package/archetypes/capabilities/product-leader.yaml +148 -0
- package/archetypes/capabilities/technical-leader.yaml +146 -0
- package/archetypes/functions/commercial-leader.yaml +62 -0
- package/archetypes/functions/compliance-officer.yaml +64 -0
- package/archetypes/functions/executive-operator.yaml +70 -0
- package/archetypes/functions/finance-leader.yaml +70 -0
- package/archetypes/functions/operations-leader.yaml +62 -0
- package/archetypes/functions/product-leader.yaml +61 -0
- package/archetypes/functions/technical-leader.yaml +57 -0
- package/bin/cohort-mcp.mjs +81 -0
- package/bin/maestro.mjs +3516 -0
- package/bin/maestro.test.mjs +1015 -0
- package/desktop-control/README.md +56 -0
- package/desktop-control/app-profiles/gmail.yaml +120 -0
- package/desktop-control/app-profiles/slack.yaml +315 -0
- package/desktop-control/app-profiles/whatsapp.yaml +107 -0
- package/docs/architecture/agent-topology.md +2239 -0
- package/docs/architecture/archetype-agent-factory.md +110 -0
- package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
- package/docs/architecture/continuous-monitoring.md +221 -0
- package/docs/architecture/mcp-capability-map.md +585 -0
- package/docs/architecture/system-architecture.md +1272 -0
- package/docs/company-context/README.md +40 -0
- package/docs/guides/agent-persona-setup.md +600 -0
- package/docs/guides/agents-observe-setup.md +64 -0
- package/docs/guides/billing-console-keys.md +88 -0
- package/docs/guides/ccxray-diagnostics.md +65 -0
- package/docs/guides/channel-bus.md +127 -0
- package/docs/guides/claude-mem-setup.md +79 -0
- package/docs/guides/claude-pace-setup.md +56 -0
- package/docs/guides/claudraband-sessions.md +98 -0
- package/docs/guides/clawteam-swarm.md +116 -0
- package/docs/guides/code-review-graph-setup.md +86 -0
- package/docs/guides/email-setup.md +431 -0
- package/docs/guides/mac-mini.md +119 -0
- package/docs/guides/media-generation-setup.md +349 -0
- package/docs/guides/model-routing.md +162 -0
- package/docs/guides/observability-otel.md +265 -0
- package/docs/guides/org-onboarding.md +132 -0
- package/docs/guides/outbound-governance-setup.md +437 -0
- package/docs/guides/pdf-generation-setup.md +315 -0
- package/docs/guides/poller-daemon-setup.md +563 -0
- package/docs/guides/rag-context-setup.md +459 -0
- package/docs/guides/self-optimization-pattern.md +82 -0
- package/docs/guides/setup-wizard.md +178 -0
- package/docs/guides/slack-setup.md +350 -0
- package/docs/guides/telegram-setup.md +227 -0
- package/docs/guides/twilio-subaccounts-setup.md +223 -0
- package/docs/guides/verification.md +128 -0
- package/docs/guides/voice-mode.md +188 -0
- package/docs/guides/voice-sms-setup.md +698 -0
- package/docs/guides/webhook-relay-setup.md +349 -0
- package/docs/guides/whatsapp-setup.md +288 -0
- package/docs/prompts/board-pack-cover-template.md +36 -0
- package/docs/prompts/decision-recommendation-template.md +88 -0
- package/docs/prompts/followup-message-template.md +141 -0
- package/docs/prompts/investor-letter-template.md +52 -0
- package/docs/prompts/morning-brief-template.md +82 -0
- package/docs/prompts/presentation-template.md +58 -0
- package/docs/prompts/weekly-strategic-memo-template.md +104 -0
- package/docs/research/hallucinated-tool-output-investigation.md +151 -0
- package/docs/runbooks/backup-restore.md +205 -0
- package/docs/runbooks/cohort-cutover.md +129 -0
- package/docs/runbooks/fleet-operations.md +200 -0
- package/docs/runbooks/incident-response.md +226 -0
- package/docs/runbooks/mac-mini-bootstrap.md +431 -0
- package/docs/runbooks/perpetual-operations.md +509 -0
- package/docs/runbooks/recovery-and-failover.md +260 -0
- package/framework-features.json +267 -0
- package/ingest/README.md +87 -0
- package/lib/action-executor.js +689 -0
- package/lib/action-executor.test.mjs +871 -0
- package/lib/agent-root.mjs +37 -0
- package/lib/archetype.mjs +236 -0
- package/lib/archetype.test.mjs +132 -0
- package/lib/autonomy.mjs +114 -0
- package/lib/autonomy.test.mjs +66 -0
- package/lib/backlog.mjs +358 -0
- package/lib/backlog.test.mjs +266 -0
- package/lib/budget-guard.mjs +279 -0
- package/lib/budget-guard.test.mjs +291 -0
- package/lib/cadence-bus-schedule.test.mjs +194 -0
- package/lib/cadence-bus.mjs +1120 -0
- package/lib/cadence-bus.test.mjs +720 -0
- package/lib/cadences.mjs +205 -0
- package/lib/cadences.test.mjs +125 -0
- package/lib/capability.mjs +154 -0
- package/lib/capability.test.mjs +78 -0
- package/lib/channels/base-adapter.mjs +719 -0
- package/lib/channels/base-adapter.test.mjs +590 -0
- package/lib/channels/channel.mjs +128 -0
- package/lib/channels/channels.test.mjs +371 -0
- package/lib/channels/contract.mjs +215 -0
- package/lib/channels/contract.test.mjs +137 -0
- package/lib/channels/conversation-resolver.mjs +95 -0
- package/lib/channels/gmail/adapter.mjs +87 -0
- package/lib/channels/inbox-item.mjs +255 -0
- package/lib/channels/inbox-item.test.mjs +335 -0
- package/lib/channels/index.mjs +94 -0
- package/lib/channels/orgmail/adapter.mjs +353 -0
- package/lib/channels/orgmail/adapter.test.mjs +311 -0
- package/lib/channels/pairing.mjs +363 -0
- package/lib/channels/pairing.test.mjs +270 -0
- package/lib/channels/registry.mjs +164 -0
- package/lib/channels/slack/adapter.mjs +317 -0
- package/lib/channels/slack-adapter.test.mjs +212 -0
- package/lib/channels/sms/adapter.mjs +43 -0
- package/lib/channels/telegram/adapter.mjs +432 -0
- package/lib/channels/telegram-adapter.test.mjs +306 -0
- package/lib/channels/voice/adapter.mjs +301 -0
- package/lib/channels/voice/adapter.test.mjs +278 -0
- package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
- package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
- package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
- package/lib/charter.mjs +256 -0
- package/lib/charter.test.mjs +89 -0
- package/lib/claude-bin.mjs +134 -0
- package/lib/claude-bin.test.mjs +75 -0
- package/lib/collective/capture.mjs +185 -0
- package/lib/collective/capture.test.mjs +121 -0
- package/lib/collective/cards.mjs +201 -0
- package/lib/collective/cards.test.mjs +114 -0
- package/lib/collective/config.mjs +186 -0
- package/lib/collective/config.test.mjs +123 -0
- package/lib/collective/global-config.mjs +113 -0
- package/lib/collective/global-config.test.mjs +75 -0
- package/lib/collective/presence.mjs +201 -0
- package/lib/collective/presence.test.mjs +95 -0
- package/lib/collective/recall.mjs +215 -0
- package/lib/collective/recall.test.mjs +116 -0
- package/lib/comms/send-gate.mjs +554 -0
- package/lib/comms/send-gate.test.mjs +577 -0
- package/lib/comms.mjs +67 -0
- package/lib/comms.test.mjs +41 -0
- package/lib/diagnostics/alerts.mjs +424 -0
- package/lib/diagnostics/alerts.test.mjs +318 -0
- package/lib/diagnostics/backup-freshness.mjs +188 -0
- package/lib/diagnostics/backup-freshness.test.mjs +185 -0
- package/lib/diagnostics/counters.mjs +269 -0
- package/lib/diagnostics/counters.test.mjs +206 -0
- package/lib/diagnostics/events.mjs +188 -0
- package/lib/diagnostics/events.test.mjs +290 -0
- package/lib/diagnostics/otel.mjs +237 -0
- package/lib/diagnostics/otel.test.mjs +196 -0
- package/lib/diagnostics/trace.mjs +216 -0
- package/lib/diagnostics/trace.test.mjs +251 -0
- package/lib/env-compat.mjs +74 -0
- package/lib/env-compat.test.mjs +104 -0
- package/lib/feature-init.mjs +331 -0
- package/lib/fs-atomic.mjs +112 -0
- package/lib/fs-atomic.test.mjs +72 -0
- package/lib/fs-ownership.mjs +111 -0
- package/lib/fs-ownership.test.mjs +158 -0
- package/lib/hooks/bus.mjs +347 -0
- package/lib/hooks/bus.test.mjs +387 -0
- package/lib/index.js +16 -0
- package/lib/learning/config.mjs +106 -0
- package/lib/learning/config.test.mjs +75 -0
- package/lib/learning/counters.mjs +156 -0
- package/lib/learning/counters.test.mjs +69 -0
- package/lib/learning/curator-consolidate.test.mjs +238 -0
- package/lib/learning/curator.mjs +453 -0
- package/lib/learning/curator.test.mjs +106 -0
- package/lib/learning/log.mjs +40 -0
- package/lib/learning/reflect.mjs +534 -0
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.mjs +352 -0
- package/lib/learning/session-index.test.mjs +125 -0
- package/lib/learning/skill-writer.mjs +474 -0
- package/lib/learning/skill-writer.test.mjs +210 -0
- package/lib/mcp/server.mjs +328 -0
- package/lib/mcp/server.test.mjs +400 -0
- package/lib/model-router/auth-profiles.mjs +758 -0
- package/lib/model-router/auth-profiles.test.mjs +580 -0
- package/lib/model-router/catalog/anthropic.yaml +153 -0
- package/lib/model-router/catalog/deepseek.yaml +86 -0
- package/lib/model-router/catalog/moonshot.yaml +81 -0
- package/lib/model-router/catalog/qwen.yaml +114 -0
- package/lib/model-router/catalog.mjs +925 -0
- package/lib/model-router/catalog.test.mjs +385 -0
- package/lib/model-router/economics.mjs +564 -0
- package/lib/model-router/economics.test.mjs +344 -0
- package/lib/model-router/failover.mjs +298 -0
- package/lib/model-router/failover.test.mjs +439 -0
- package/lib/model-router/health.mjs +453 -0
- package/lib/model-router/health.test.mjs +338 -0
- package/lib/model-router/integration-coverage.test.mjs +829 -0
- package/lib/model-router/integration.test.mjs +564 -0
- package/lib/model-router/ledger.mjs +402 -0
- package/lib/model-router/ledger.test.mjs +382 -0
- package/lib/model-router/llm-task.mjs +515 -0
- package/lib/model-router/llm-task.test.mjs +392 -0
- package/lib/model-router/org-credentials.mjs +260 -0
- package/lib/model-router/org-credentials.test.mjs +265 -0
- package/lib/model-router/pricing-refresh.mjs +463 -0
- package/lib/model-router/pricing-refresh.test.mjs +286 -0
- package/lib/model-router/reconcile.mjs +429 -0
- package/lib/model-router/reconcile.test.mjs +316 -0
- package/lib/model-router/repair.mjs +471 -0
- package/lib/model-router/repair.test.mjs +180 -0
- package/lib/model-router/resolve.mjs +1206 -0
- package/lib/model-router/spawn.mjs +497 -0
- package/lib/model-router/spawn.test.mjs +425 -0
- package/lib/model-router/taxonomy.mjs +893 -0
- package/lib/model-router/taxonomy.test.mjs +410 -0
- package/lib/model-router.mjs +677 -0
- package/lib/model-router.test.mjs +907 -0
- package/lib/org/activity.mjs +211 -0
- package/lib/org/activity.test.mjs +134 -0
- package/lib/org/approvals.mjs +448 -0
- package/lib/org/approvals.test.mjs +216 -0
- package/lib/org/awareness.mjs +222 -0
- package/lib/org/awareness.test.mjs +159 -0
- package/lib/org/board.mjs +229 -0
- package/lib/org/board.test.mjs +177 -0
- package/lib/org/bootstrap-context.mjs +169 -0
- package/lib/org/bootstrap-context.test.mjs +153 -0
- package/lib/org/client.mjs +1628 -0
- package/lib/org/client.test.mjs +1107 -0
- package/lib/org/cohort-client.mjs +67 -0
- package/lib/org/cohort-client.test.mjs +126 -0
- package/lib/org/cost-sync.mjs +227 -0
- package/lib/org/cost-sync.test.mjs +153 -0
- package/lib/org/doctor.mjs +212 -0
- package/lib/org/doctor.test.mjs +212 -0
- package/lib/org/handoff.mjs +293 -0
- package/lib/org/handoff.test.mjs +269 -0
- package/lib/org/integration-tools.mjs +182 -0
- package/lib/org/integration-tools.test.mjs +160 -0
- package/lib/org/keys.mjs +131 -0
- package/lib/org/keys.test.mjs +92 -0
- package/lib/org/knowledge.mjs +463 -0
- package/lib/org/knowledge.test.mjs +319 -0
- package/lib/org/leases.mjs +335 -0
- package/lib/org/leases.test.mjs +235 -0
- package/lib/org/mesh-integration.test.mjs +127 -0
- package/lib/org/mesh.mjs +459 -0
- package/lib/org/mesh.test.mjs +345 -0
- package/lib/org/messaging.mjs +503 -0
- package/lib/org/messaging.test.mjs +238 -0
- package/lib/org/policy.mjs +345 -0
- package/lib/org/policy.test.mjs +237 -0
- package/lib/org/protocol.checksum +1 -0
- package/lib/org/protocol.checksum.test.mjs +90 -0
- package/lib/org/protocol.mjs +967 -0
- package/lib/org/protocol.test.mjs +264 -0
- package/lib/org/registry.mjs +194 -0
- package/lib/org/registry.test.mjs +100 -0
- package/lib/org/tool-surface-integration.test.mjs +120 -0
- package/lib/org/tool-surface.mjs +2535 -0
- package/lib/org/tool-surface.test.mjs +589 -0
- package/lib/org/ui-parity.mjs +3236 -0
- package/lib/org/ui-parity.test.mjs +348 -0
- package/lib/org/verify.mjs +176 -0
- package/lib/org/verify.test.mjs +194 -0
- package/lib/rag/embed.mjs +188 -0
- package/lib/rag/indexer.mjs +425 -0
- package/lib/rag/rag.test.mjs +505 -0
- package/lib/rag/search.mjs +475 -0
- package/lib/rate-guard.mjs +246 -0
- package/lib/rate-guard.test.mjs +201 -0
- package/lib/render.mjs +112 -0
- package/lib/render.test.mjs +68 -0
- package/lib/resource-governor.mjs +297 -0
- package/lib/resource-governor.test.mjs +262 -0
- package/lib/scheduling/dynamic-jobs.mjs +675 -0
- package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
- package/lib/scheduling/jitter.mjs +0 -0
- package/lib/scheduling/jitter.test.mjs +140 -0
- package/lib/secrets/broker.mjs +315 -0
- package/lib/secrets/broker.test.mjs +280 -0
- package/lib/secrets/providers.mjs +461 -0
- package/lib/secrets/providers.test.mjs +274 -0
- package/lib/security/audit-engine.mjs +684 -0
- package/lib/security/audit-engine.test.mjs +389 -0
- package/lib/security/coerce-args.mjs +552 -0
- package/lib/security/coerce-args.test.mjs +281 -0
- package/lib/security/dangerous-tools.mjs +97 -0
- package/lib/security/dangerous-tools.test.mjs +68 -0
- package/lib/security/external-content.mjs +145 -0
- package/lib/security/external-content.test.mjs +67 -0
- package/lib/security/redact.mjs +592 -0
- package/lib/security/redact.test.mjs +441 -0
- package/lib/security/secret-equal.mjs +73 -0
- package/lib/security/secret-equal.test.mjs +55 -0
- package/lib/session-permissions.mjs +101 -0
- package/lib/session-permissions.test.mjs +100 -0
- package/lib/setup/claude-probe.mjs +74 -0
- package/lib/setup/completeness.mjs +175 -0
- package/lib/setup/completeness.test.mjs +110 -0
- package/lib/setup/context-pack.mjs +173 -0
- package/lib/setup/context-pack.test.mjs +89 -0
- package/lib/setup/enrich.mjs +277 -0
- package/lib/setup/enrich.test.mjs +115 -0
- package/lib/setup/enroll-from-cohort.mjs +441 -0
- package/lib/setup/enroll-from-cohort.test.mjs +233 -0
- package/lib/setup/integration.test.mjs +162 -0
- package/lib/setup/io.mjs +360 -0
- package/lib/setup/io.test.mjs +77 -0
- package/lib/setup/run-generator.mjs +81 -0
- package/lib/setup/runner.mjs +244 -0
- package/lib/setup/runner.test.mjs +132 -0
- package/lib/setup/sections/comms.mjs +173 -0
- package/lib/setup/sections/company.mjs +120 -0
- package/lib/setup/sections/enrich.mjs +138 -0
- package/lib/setup/sections/identity.mjs +182 -0
- package/lib/setup/sections/identity.test.mjs +140 -0
- package/lib/setup/sections/learning.mjs +153 -0
- package/lib/setup/sections/learning.test.mjs +81 -0
- package/lib/setup/sections/messaging.mjs +219 -0
- package/lib/setup/sections/messaging.test.mjs +127 -0
- package/lib/setup/sections/model.mjs +102 -0
- package/lib/setup/sections/operating-model.mjs +78 -0
- package/lib/setup/sections/org.mjs +475 -0
- package/lib/setup/sections/org.test.mjs +313 -0
- package/lib/setup/sections/orgmail.mjs +173 -0
- package/lib/setup/sections/orgmail.test.mjs +118 -0
- package/lib/setup/sections/recovery.mjs +159 -0
- package/lib/setup/sections/recovery.test.mjs +98 -0
- package/lib/setup/sections/tools.mjs +132 -0
- package/lib/setup/sections/verify.mjs +97 -0
- package/lib/setup/sot.mjs +205 -0
- package/lib/setup/sot.test.mjs +81 -0
- package/lib/setup/state.mjs +151 -0
- package/lib/setup/state.test.mjs +92 -0
- package/lib/singleton.js +229 -0
- package/lib/singleton.test.mjs +135 -0
- package/lib/telemetry/alerts.mjs +216 -0
- package/lib/telemetry/alerts.test.mjs +109 -0
- package/lib/telemetry/collect.mjs +512 -0
- package/lib/telemetry/collect.test.mjs +202 -0
- package/lib/tool-definitions-integration.test.mjs +83 -0
- package/lib/tool-definitions.js +738 -0
- package/lib/tool-definitions.test.mjs +437 -0
- package/lib/util/fetch-timeout.mjs +136 -0
- package/lib/util/fetch-timeout.test.mjs +202 -0
- package/lib/util/reconnect.mjs +343 -0
- package/lib/util/reconnect.test.mjs +369 -0
- package/lib/util/unhandled.mjs +205 -0
- package/lib/util/unhandled.test.mjs +216 -0
- package/lib/voice/context-loader.mjs +466 -0
- package/lib/voice/index.mjs +100 -0
- package/lib/voice/openai-realtime.mjs +510 -0
- package/lib/voice/outbound.mjs +542 -0
- package/lib/voice/outbound.test.mjs +69 -0
- package/lib/voice/post-call-brief.mjs +428 -0
- package/lib/voice/provider.mjs +52 -0
- package/lib/voice/session-rotation.mjs +257 -0
- package/lib/voice/stt.mjs +161 -0
- package/lib/voice/stt.test.mjs +226 -0
- package/lib/voice/tool-bridge.mjs +370 -0
- package/lib/voice/tts.mjs +104 -0
- package/lib/voice/twilio-sip-bridge.mjs +288 -0
- package/lib/voice/voice.test.mjs +990 -0
- package/mcp/README.md +80 -0
- package/package.json +151 -0
- package/plugins/maestro-skills/plugin.json +139 -0
- package/plugins/maestro-skills/skills/agents-observe.md +110 -0
- package/plugins/maestro-skills/skills/board-deck.md +68 -0
- package/plugins/maestro-skills/skills/books-close.md +77 -0
- package/plugins/maestro-skills/skills/brand-steward.md +121 -0
- package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
- package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
- package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
- package/plugins/maestro-skills/skills/claude-pace.md +61 -0
- package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
- package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
- package/plugins/maestro-skills/skills/decision-brief.md +89 -0
- package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
- package/plugins/maestro-skills/skills/draft-comms.md +84 -0
- package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
- package/plugins/maestro-skills/skills/files-find.md +65 -0
- package/plugins/maestro-skills/skills/generative-ui.md +228 -0
- package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
- package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
- package/plugins/maestro-skills/skills/mail-triage.md +86 -0
- package/plugins/maestro-skills/skills/morning-brief.md +54 -0
- package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
- package/plugins/maestro-skills/skills/org-board.md +133 -0
- package/plugins/maestro-skills/skills/org-credential.md +68 -0
- package/plugins/maestro-skills/skills/org-recall.md +81 -0
- package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
- package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
- package/plugins/maestro-skills/skills/router-why.md +78 -0
- package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
- package/plugins/maestro-skills/skills/session-search.md +71 -0
- package/plugins/maestro-skills/skills/set-reminder.md +93 -0
- package/plugins/maestro-skills/skills/slack-followup.md +64 -0
- package/plugins/maestro-skills/skills/team-activity.md +86 -0
- package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
- package/policies/action-classification.yaml +114 -0
- package/policies/ai-disclosure.yaml +294 -0
- package/policies/communication-style.md +139 -0
- package/policies/information-barriers.yaml +118 -0
- package/policies/prompt-injection-defence.yaml +138 -0
- package/public/assets/icon-dark.png +0 -0
- package/public/assets/icon-dark.svg +9 -0
- package/public/assets/icon-light.svg +9 -0
- package/public/assets/logo-dark.svg +15 -0
- package/public/assets/logo-light.svg +15 -0
- package/scaffold/.mcp.json +7 -0
- package/scaffold/CLAUDE.md +368 -0
- package/scaffold/config/agent.json +55 -0
- package/scaffold/config/agent.ts +76 -0
- package/scaffold/config/agent.ts.example +89 -0
- package/scaffold/config/alerts.yaml +23 -0
- package/scaffold/config/allowlist.yaml.example +25 -0
- package/scaffold/config/caller-id-map.yaml +46 -0
- package/scaffold/config/collective.yaml +49 -0
- package/scaffold/config/company.json +20 -0
- package/scaffold/config/known-agents.json +6 -0
- package/scaffold/config/learning.yaml +55 -0
- package/scaffold/config/model-routing.yaml.example +104 -0
- package/scaffold/config/org.yaml +25 -0
- package/scaffold/config/orgmail.yaml.example +19 -0
- package/scaffold/config/recovery.yaml +72 -0
- package/scaffold/config/secrets.yaml +27 -0
- package/scaffold/config/slack.yaml.example +35 -0
- package/scaffold/config/telegram.yaml.example +38 -0
- package/scaffold/config/voice.yaml.example +89 -0
- package/scaffold/config/whatsapp.yaml.example +39 -0
- package/schedules/README.md +49 -0
- package/schedules/triggers/backlog-executor.md +102 -0
- package/schedules/triggers/brand-steward.md +72 -0
- package/schedules/triggers/daily-evening-wrap.md +159 -0
- package/schedules/triggers/daily-midday-sweep.md +58 -0
- package/schedules/triggers/daily-morning-brief.md +55 -0
- package/schedules/triggers/directory-hygiene.md +81 -0
- package/schedules/triggers/dynamic-jobs.md +40 -0
- package/schedules/triggers/inbox-processor.md +115 -0
- package/schedules/triggers/meeting-action-capture.md +60 -0
- package/schedules/triggers/meeting-prep.md +69 -0
- package/schedules/triggers/messaging-inbound.md +50 -0
- package/schedules/triggers/org-pulse.md +24 -0
- package/schedules/triggers/quarterly-self-assessment.md +54 -0
- package/schedules/triggers/weekly-engineering-health.md +37 -0
- package/schedules/triggers/weekly-execution.md +65 -0
- package/schedules/triggers/weekly-hiring.md +53 -0
- package/schedules/triggers/weekly-priorities.md +38 -0
- package/schedules/triggers/weekly-strategic-memo.md +124 -0
- package/scripts/archive-email.sh +55 -0
- package/scripts/cadence/cadence-status.mjs +36 -0
- package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
- package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
- package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
- package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
- package/scripts/ci/check-docs-accuracy.mjs +493 -0
- package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
- package/scripts/ci/check-exports-exist.mjs +140 -0
- package/scripts/ci/check-files-exist.mjs +107 -0
- package/scripts/ci/check-no-build-artifacts.mjs +111 -0
- package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
- package/scripts/ci/check-no-confidential.mjs +198 -0
- package/scripts/ci/check-no-conflict-markers.mjs +169 -0
- package/scripts/ci/check-no-residual-identity.mjs +163 -0
- package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
- package/scripts/ci/check-tarball-fidelity.mjs +205 -0
- package/scripts/ci/check-unresolved-tokens.mjs +83 -0
- package/scripts/ci/check.mjs +109 -0
- package/scripts/ci/check.test.mjs +194 -0
- package/scripts/ci/run-coverage.mjs +82 -0
- package/scripts/ci/run-tests.mjs +71 -0
- package/scripts/cloud-relay/README.md +59 -0
- package/scripts/cloud-relay/index.mjs +233 -0
- package/scripts/cloud-relay/package.json +15 -0
- package/scripts/cloud-relay/railway.json +13 -0
- package/scripts/cloud-relay/voice/README.md +94 -0
- package/scripts/cloud-relay/voice/package-lock.json +39 -0
- package/scripts/cloud-relay/voice/package.json +16 -0
- package/scripts/cloud-relay/voice/railway.json +13 -0
- package/scripts/cloud-relay/voice/server.mjs +532 -0
- package/scripts/collective/hook-runner.mjs +211 -0
- package/scripts/collective/hook-runner.test.mjs +90 -0
- package/scripts/collective/org-pulse.mjs +72 -0
- package/scripts/collective/org-sync.mjs +61 -0
- package/scripts/collective/recall.mjs +45 -0
- package/scripts/collective/who.mjs +30 -0
- package/scripts/comms-monitor.sh +288 -0
- package/scripts/configure-whatsapp-sandbox.sh +201 -0
- package/scripts/continuous-monitor.sh +91 -0
- package/scripts/cost/fleet-digest.mjs +407 -0
- package/scripts/cost/fleet-digest.test.mjs +207 -0
- package/scripts/cost/track-claude-usage.mjs +169 -0
- package/scripts/daemon/agent-daemon.mjs +989 -0
- package/scripts/daemon/agent-daemon.test.mjs +525 -0
- package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
- package/scripts/daemon/cadence-consumer.mjs +1080 -0
- package/scripts/daemon/cadence-consumer.test.mjs +770 -0
- package/scripts/daemon/cadence-handlers.mjs +1121 -0
- package/scripts/daemon/cadence-handlers.test.mjs +617 -0
- package/scripts/daemon/classifier.mjs +704 -0
- package/scripts/daemon/classifier.test.mjs +238 -0
- package/scripts/daemon/classify-kind.mjs +54 -0
- package/scripts/daemon/classify-kind.test.mjs +40 -0
- package/scripts/daemon/context-compiler.mjs +605 -0
- package/scripts/daemon/context-compiler.test.mjs +300 -0
- package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
- package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
- package/scripts/daemon/dispatcher.mjs +1516 -0
- package/scripts/daemon/health.mjs +72 -0
- package/scripts/daemon/inbox-deferral.mjs +210 -0
- package/scripts/daemon/inbox-deferral.test.mjs +242 -0
- package/scripts/daemon/integration.test.mjs +149 -0
- package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
- package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
- package/scripts/daemon/launchd-wrapper.sh +91 -0
- package/scripts/daemon/lib/session-router.mjs +274 -0
- package/scripts/daemon/lib/session-router.test.mjs +295 -0
- package/scripts/daemon/maestro-daemon.mjs +275 -0
- package/scripts/daemon/prompt-builder.mjs +685 -0
- package/scripts/daemon/prompt-builder.test.mjs +213 -0
- package/scripts/daemon/responder.mjs +854 -0
- package/scripts/daemon/session-lock.mjs +721 -0
- package/scripts/daemon/session-lock.test.mjs +252 -0
- package/scripts/daemon/session-outcomes.mjs +640 -0
- package/scripts/daemon/session-outcomes.test.mjs +533 -0
- package/scripts/daemon/typing-registry.mjs +90 -0
- package/scripts/daemon/typing-registry.test.mjs +77 -0
- package/scripts/daemon/voice-webhook-server.mjs +804 -0
- package/scripts/decisions/capture-decision.mjs +116 -0
- package/scripts/disclosure_assessment.py +873 -0
- package/scripts/disclosure_boundaries.py +562 -0
- package/scripts/email-signature-principal.html +52 -0
- package/scripts/email-signature.html +60 -0
- package/scripts/email_quote_thread.py +167 -0
- package/scripts/email_thread_dedup.py +362 -0
- package/scripts/emergency-stop.sh +81 -0
- package/scripts/healthcheck.sh +116 -0
- package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
- package/scripts/hooks/block-mcp-slack-send.sh +7 -0
- package/scripts/hooks/post-action-log.sh +126 -0
- package/scripts/hooks/pre-send-audit.sh +174 -0
- package/scripts/hooks/pre-send-audit.test.mjs +215 -0
- package/scripts/hooks/session-end-log.sh +27 -0
- package/scripts/hooks/session-start-banner.sh +115 -0
- package/scripts/huddle/audio-bridge.mjs +664 -0
- package/scripts/huddle/boot-slack-cdp.sh +102 -0
- package/scripts/huddle/huddle-controller.mjs +942 -0
- package/scripts/huddle/huddle-server.mjs +1229 -0
- package/scripts/huddle/launch-slack.sh +232 -0
- package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
- package/scripts/huddle/package-lock.json +62 -0
- package/scripts/huddle/package.json +22 -0
- package/scripts/huddle/setup-audio.sh +239 -0
- package/scripts/huddle/start-call.mjs +318 -0
- package/scripts/huddle/test-pipeline.mjs +263 -0
- package/scripts/learning/consolidate-skills.mjs +72 -0
- package/scripts/learning/session-search.mjs +125 -0
- package/scripts/llm_email_dedup.py +442 -0
- package/scripts/local-triggers/generate-plists.sh +432 -0
- package/scripts/local-triggers/generate-plists.test.mjs +413 -0
- package/scripts/local-triggers/install-all.sh +49 -0
- package/scripts/local-triggers/plists/.gitkeep +0 -0
- package/scripts/local-triggers/run-trigger.sh +63 -0
- package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
- package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
- package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
- package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
- package/scripts/maintenance/backup-to-cloud.sh +124 -0
- package/scripts/maintenance/health-check.sh +377 -0
- package/scripts/media-generation/README.md +105 -0
- package/scripts/media-generation/gemini-image-client.mjs +173 -0
- package/scripts/media-generation/generate-assets.mjs +289 -0
- package/scripts/media-generation/veo-video-client.mjs +219 -0
- package/scripts/org/send-orgmail.mjs +227 -0
- package/scripts/outbound-dedup-cleanup.sh +43 -0
- package/scripts/outbound-dedup.sh +477 -0
- package/scripts/outbound_dedup.py +115 -0
- package/scripts/parse-voice-transcript.mjs +481 -0
- package/scripts/pdf-generation/README.md +63 -0
- package/scripts/pdf-generation/build-document.mjs +247 -0
- package/scripts/pdf-generation/templates/board-pack.latex +136 -0
- package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
- package/scripts/pdf-generation/templates/memo.latex +114 -0
- package/scripts/poll-slack-events.sh +35 -0
- package/scripts/poller/calendar-poller.mjs +12 -0
- package/scripts/poller/gmail-poller.mjs +192 -0
- package/scripts/poller/imap-client.mjs +289 -0
- package/scripts/poller/inbox-scan-poller.mjs +156 -0
- package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
- package/scripts/poller/index.mjs +73 -0
- package/scripts/poller/intra-session-check.mjs +285 -0
- package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
- package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
- package/scripts/poller/secondary-gmail-poller.mjs +132 -0
- package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
- package/scripts/poller/slack-poller.mjs +854 -0
- package/scripts/poller/slack-socket-mode.mjs +917 -0
- package/scripts/poller/slack-socket-mode.test.mjs +753 -0
- package/scripts/poller/trigger.mjs +75 -0
- package/scripts/poller/utils.mjs +371 -0
- package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
- package/scripts/poller/voice-poller.mjs +236 -0
- package/scripts/poller-launchd/install.sh +66 -0
- package/scripts/poller-launchd/poller.plist.template +40 -0
- package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
- package/scripts/post-interaction-indexer.py +1598 -0
- package/scripts/pre-draft-context.py +994 -0
- package/scripts/pre_draft_lookup.py +258 -0
- package/scripts/rag/build-index.mjs +47 -0
- package/scripts/rag/ingest.mjs +111 -0
- package/scripts/rag/search.mjs +119 -0
- package/scripts/rag-indexer.py +629 -0
- package/scripts/restore-from-backup.sh +248 -0
- package/scripts/restore-from-backup.test.mjs +178 -0
- package/scripts/resume-operations.sh +80 -0
- package/scripts/search-secondary-inbox.py +181 -0
- package/scripts/secondary-inbox-poller.py +437 -0
- package/scripts/self-optimization/compute-metrics.py +398 -0
- package/scripts/send-email-as-principal.py +369 -0
- package/scripts/send-email-threaded.py +392 -0
- package/scripts/send-email-with-attachment.py +377 -0
- package/scripts/send-email.sh +131 -0
- package/scripts/send-sms.sh +175 -0
- package/scripts/send-whatsapp.sh +292 -0
- package/scripts/session-start.sh +106 -0
- package/scripts/setup/boot-claude-session.sh +94 -0
- package/scripts/setup/configure-macos.sh +674 -0
- package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
- package/scripts/setup/configure-voice-tunnel.mjs +182 -0
- package/scripts/setup/generate-agent-env.mjs +92 -0
- package/scripts/setup/generate-agent-package-json.mjs +222 -0
- package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
- package/scripts/setup/generate-autonomy.mjs +60 -0
- package/scripts/setup/generate-backlog.mjs +101 -0
- package/scripts/setup/generate-cadences.mjs +92 -0
- package/scripts/setup/generate-capability.mjs +162 -0
- package/scripts/setup/generate-charter.mjs +76 -0
- package/scripts/setup/generate-comms.mjs +59 -0
- package/scripts/setup/generate-company.mjs +92 -0
- package/scripts/setup/init-agent.sh +547 -0
- package/scripts/setup/init-agent.test.mjs +151 -0
- package/scripts/setup/init-archetype.mjs +74 -0
- package/scripts/setup/init-backup.mjs +54 -0
- package/scripts/setup/init-cadence-bus.mjs +60 -0
- package/scripts/setup/init-channel-bus.mjs +46 -0
- package/scripts/setup/init-cost-tracking.mjs +45 -0
- package/scripts/setup/init-decision-capture.mjs +66 -0
- package/scripts/setup/init-known-agents.mjs +57 -0
- package/scripts/setup/init-learning.mjs +70 -0
- package/scripts/setup/init-memory-executive.mjs +45 -0
- package/scripts/setup/init-model-router.mjs +124 -0
- package/scripts/setup/init-rag.mjs +174 -0
- package/scripts/setup/init-session-router.mjs +38 -0
- package/scripts/setup/init-slack-socket-mode.mjs +260 -0
- package/scripts/setup/init-telegram.mjs +165 -0
- package/scripts/setup/init-voice-realtime.mjs +204 -0
- package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
- package/scripts/setup/install-dev-tools.sh +150 -0
- package/scripts/setup/lib/install-plist.mjs +95 -0
- package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
- package/scripts/setup/render-environment-yaml.mjs +133 -0
- package/scripts/slack-events-ctl.sh +177 -0
- package/scripts/slack-events-server.mjs +1045 -0
- package/scripts/slack-react.mjs +89 -0
- package/scripts/slack-responded.sh +232 -0
- package/scripts/slack-send.sh +287 -0
- package/scripts/slack-typing.mjs +196 -0
- package/scripts/slack-upload-v2.py +95 -0
- package/scripts/sms-handler.mjs +450 -0
- package/scripts/spawn-session.sh +120 -0
- package/scripts/sync-protocol.mjs +217 -0
- package/scripts/system-verify.sh +184 -0
- package/scripts/test-email-thread-dedup.py +239 -0
- package/scripts/test-information-barriers.py +484 -0
- package/scripts/test-llm-email-dedup.py +251 -0
- package/scripts/test-pre-draft-integration.py +203 -0
- package/scripts/test-rag-phase2.sh +442 -0
- package/scripts/test-rag-search.sh +251 -0
- package/scripts/test-voice-parser.mjs +316 -0
- package/scripts/user-context-search.py +659 -0
- package/scripts/validate_outbound.py +1504 -0
- package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
- package/scripts/watchdog/force-reboot.sh +157 -0
- package/scripts/watchdog/memory-watchdog.sh +473 -0
- package/scripts/whatsapp-handler.mjs +538 -0
- package/teams/desktop-operations.yaml +34 -0
- package/teams/executive-office.yaml +27 -0
- package/teams/legal-and-regulatory.yaml +24 -0
- package/teams/platform-and-engineering.yaml +23 -0
- package/teams/strategy-and-growth.yaml +29 -0
- package/workflows/continuous/backlog-executor.yaml +141 -0
- package/workflows/continuous/inbound-monitor.yaml +168 -0
- package/workflows/daily/applicant-triage.yaml +197 -0
- package/workflows/daily/comms-triage.yaml +80 -0
- package/workflows/daily/evening-wrap.yaml +105 -0
- package/workflows/daily/morning-brief.yaml +164 -0
- package/workflows/daily/slack-followup-sweep.yaml +87 -0
- package/workflows/event-driven/README.md +50 -0
- package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
- package/workflows/event-driven/pr-review.yaml +107 -0
- package/workflows/monthly/board-readiness.yaml +76 -0
- package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
- package/workflows/session-protocol.md +171 -0
- package/workflows/weekly/engineering-health.yaml +154 -0
- package/workflows/weekly/hiring-review.yaml +169 -0
- package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
- package/workflows/weekly/strategic-memo.yaml +79 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/org/awareness.mjs — situational-awareness SDK over the cached org context.
|
|
3
|
+
*
|
|
4
|
+
* PURE read + summarise over the `bootstrapContext` object (or the persisted
|
|
5
|
+
* `config/org-context.json`). ZERO LLM, ZERO network, ZERO I/O beyond optionally
|
|
6
|
+
* loading the cache file. Gives a provisioned agent cheap, deterministic answers
|
|
7
|
+
* to "who owns this?", "who do I escalate to?", "who reports to me?", "who's on
|
|
8
|
+
* this team?", and "which adopted decisions touch this topic?" — drawn from the
|
|
9
|
+
* org's authoritative structure rather than guessed from the local archetype.
|
|
10
|
+
*
|
|
11
|
+
* Fail-open by contract: every helper tolerates a null/partial context and a
|
|
12
|
+
* missing part, and returns `null` (single answer) or `[]` (list) when the data
|
|
13
|
+
* is absent — NEVER throws. The org plane being degraded simply means the agent
|
|
14
|
+
* gets a null/empty answer and proceeds on its local model.
|
|
15
|
+
*
|
|
16
|
+
* Each function accepts either the bootstrapContext object directly, or an
|
|
17
|
+
* `agentRoot` string (in which case it loads `config/org-context.json`).
|
|
18
|
+
*
|
|
19
|
+
* @module lib/org/awareness
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
"use strict";
|
|
23
|
+
|
|
24
|
+
import { loadOrgContext } from "./bootstrap-context.mjs";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the first arg into a context object. A string is treated as an
|
|
28
|
+
* agentRoot and loaded from disk; an object is used as-is; anything else → {}.
|
|
29
|
+
* @param {object|string|null} ctxOrRoot
|
|
30
|
+
* @returns {object}
|
|
31
|
+
*/
|
|
32
|
+
function resolveCtx(ctxOrRoot) {
|
|
33
|
+
if (typeof ctxOrRoot === "string") return loadOrgContext(ctxOrRoot) || {};
|
|
34
|
+
if (ctxOrRoot && typeof ctxOrRoot === "object") return ctxOrRoot;
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function lc(v) {
|
|
39
|
+
return v == null ? "" : String(v).toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A node/edge ref → a compact { slug, displayName } summary (or null). */
|
|
43
|
+
function refSummary(ref) {
|
|
44
|
+
if (!ref || typeof ref !== "object") return null;
|
|
45
|
+
const slug = ref.slug ? String(ref.slug) : "";
|
|
46
|
+
if (!slug) return null;
|
|
47
|
+
return { slug, displayName: ref.displayName != null ? ref.displayName : null };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Who owns a scope? Resolves the accountable owner of a strategy stream matching
|
|
52
|
+
* `scope` (by stream code/name/mission, case-insensitive substring). Prefers the
|
|
53
|
+
* RACI "Accountable" assignment, then "Responsible", then the first assigned
|
|
54
|
+
* member. Returns a summary or null when there is no strategy / no match / no
|
|
55
|
+
* assigned member.
|
|
56
|
+
* @param {object|string} ctxOrRoot
|
|
57
|
+
* @param {string} scope
|
|
58
|
+
* @returns {{scope:string, stream:{code:string|null,name:string|null}, owner:{slug:string,displayName:string|null}, role:string}|null}
|
|
59
|
+
*/
|
|
60
|
+
export function ownerOf(ctxOrRoot, scope) {
|
|
61
|
+
const ctx = resolveCtx(ctxOrRoot);
|
|
62
|
+
const strategy = ctx.strategy;
|
|
63
|
+
const needle = lc(scope).trim();
|
|
64
|
+
if (!strategy || !Array.isArray(strategy.streams) || !needle) return null;
|
|
65
|
+
|
|
66
|
+
const stream = strategy.streams.find((s) => {
|
|
67
|
+
if (!s || typeof s !== "object") return false;
|
|
68
|
+
return lc(s.code).includes(needle) || lc(s.name).includes(needle) || lc(s.mission).includes(needle);
|
|
69
|
+
});
|
|
70
|
+
if (!stream || !Array.isArray(stream.raci) || stream.raci.length === 0) return null;
|
|
71
|
+
|
|
72
|
+
const byRole = (re) => stream.raci.find((a) => a && a.member && re.test(lc(a.role)));
|
|
73
|
+
const pick = byRole(/^a\b|accountable/) || byRole(/^r\b|responsible/) || stream.raci.find((a) => a && a.member);
|
|
74
|
+
const owner = pick && refSummary(pick.member);
|
|
75
|
+
if (!owner) return null;
|
|
76
|
+
return {
|
|
77
|
+
scope: String(scope),
|
|
78
|
+
stream: { code: stream.code != null ? stream.code : null, name: stream.name != null ? stream.name : null },
|
|
79
|
+
owner,
|
|
80
|
+
role: pick.role != null ? String(pick.role) : "",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Who does THIS agent escalate to? Draws on the self profile's authoritative
|
|
86
|
+
* `escalationPath` (free text) and supervisor / reporting-line manager. Returns
|
|
87
|
+
* `{ escalationPath, manager }` (either may be null), or null when neither the
|
|
88
|
+
* profile nor relationships carry an escalation target.
|
|
89
|
+
* @param {object|string} ctxOrRoot
|
|
90
|
+
* @returns {{escalationPath:string|null, manager:{slug:string,displayName:string|null}|null}|null}
|
|
91
|
+
*/
|
|
92
|
+
export function escalateTo(ctxOrRoot) {
|
|
93
|
+
const ctx = resolveCtx(ctxOrRoot);
|
|
94
|
+
const profile = ctx.profile || {};
|
|
95
|
+
const rel = ctx.relationships || {};
|
|
96
|
+
|
|
97
|
+
const escalationPath =
|
|
98
|
+
profile.escalationPath && String(profile.escalationPath).trim() ? String(profile.escalationPath).trim() : null;
|
|
99
|
+
const manager =
|
|
100
|
+
refSummary(profile.supervisor) ||
|
|
101
|
+
refSummary(profile.reportingLine && profile.reportingLine.manager) ||
|
|
102
|
+
refSummary(rel.reportingLine && rel.reportingLine.manager);
|
|
103
|
+
|
|
104
|
+
if (!escalationPath && !manager) return null;
|
|
105
|
+
return { escalationPath, manager };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Who reports to `selfId`? Prefers the self profile's authoritative `reports`
|
|
110
|
+
* (when `selfId` is the agent itself or omitted), then the relationships DTO,
|
|
111
|
+
* then derives from the org chart's reporting edges (manager `from` → report
|
|
112
|
+
* `to`). Returns `[]` when there are no reports / no data.
|
|
113
|
+
* @param {object|string} ctxOrRoot
|
|
114
|
+
* @param {string} [selfId] the manager's member slug
|
|
115
|
+
* @returns {Array<{slug:string, displayName:string|null, style:string|null}>}
|
|
116
|
+
*/
|
|
117
|
+
export function whoReportsToMe(ctxOrRoot, selfId) {
|
|
118
|
+
const ctx = resolveCtx(ctxOrRoot);
|
|
119
|
+
const profile = ctx.profile || {};
|
|
120
|
+
const rel = ctx.relationships || {};
|
|
121
|
+
const self = selfId ? String(selfId) : "";
|
|
122
|
+
|
|
123
|
+
const fromReportDtos = (arr) =>
|
|
124
|
+
(Array.isArray(arr) ? arr : [])
|
|
125
|
+
.map((r) => {
|
|
126
|
+
const ref = refSummary(r && r.member);
|
|
127
|
+
if (!ref) return null;
|
|
128
|
+
return { ...ref, style: r && r.style != null ? String(r.style) : null };
|
|
129
|
+
})
|
|
130
|
+
.filter(Boolean);
|
|
131
|
+
|
|
132
|
+
// 1) self profile's reports (when it IS the queried member, or no id given).
|
|
133
|
+
if (Array.isArray(profile.reports) && profile.reports.length && (!self || lc(profile.slug) === lc(self))) {
|
|
134
|
+
return fromReportDtos(profile.reports);
|
|
135
|
+
}
|
|
136
|
+
// 2) relationships DTO for the queried member.
|
|
137
|
+
if (rel.member && (!self || lc(rel.member.slug) === lc(self)) && Array.isArray(rel.reports) && rel.reports.length) {
|
|
138
|
+
return fromReportDtos(rel.reports);
|
|
139
|
+
}
|
|
140
|
+
// 3) derive from chart reporting edges (from = manager, to = report).
|
|
141
|
+
const chart = ctx.chart;
|
|
142
|
+
if (self && chart && Array.isArray(chart.edges)) {
|
|
143
|
+
const nameBySlug = new Map(
|
|
144
|
+
(Array.isArray(chart.nodes) ? chart.nodes : []).map((n) => [String(n.slug), n.displayName != null ? n.displayName : null])
|
|
145
|
+
);
|
|
146
|
+
const out = [];
|
|
147
|
+
for (const e of chart.edges) {
|
|
148
|
+
if (e && e.kind === "reporting" && lc(e.from) === lc(self)) {
|
|
149
|
+
const slug = String(e.to);
|
|
150
|
+
out.push({ slug, displayName: nameBySlug.has(slug) ? nameBySlug.get(slug) : null, style: e.type != null ? String(e.type) : null });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Who is on `team`? Filters the org-chart nodes by team (slug / name / shortLabel,
|
|
160
|
+
* case-insensitive). Falls back to the hierarchy members when no chart is present.
|
|
161
|
+
* Returns `[]` when there is no data / no match.
|
|
162
|
+
* @param {object|string} ctxOrRoot
|
|
163
|
+
* @param {string} team team slug, name, or short label
|
|
164
|
+
* @returns {Array<{slug:string, displayName:string|null, role:string|null, team:string|null}>}
|
|
165
|
+
*/
|
|
166
|
+
export function colleaguesInTeam(ctxOrRoot, team) {
|
|
167
|
+
const ctx = resolveCtx(ctxOrRoot);
|
|
168
|
+
const needle = lc(team).trim();
|
|
169
|
+
if (!needle) return [];
|
|
170
|
+
|
|
171
|
+
const matchTeam = (t) =>
|
|
172
|
+
!!t && (lc(t.slug) === needle || lc(t.name) === needle || lc(t.shortLabel) === needle || lc(t.name).includes(needle));
|
|
173
|
+
|
|
174
|
+
const chart = ctx.chart;
|
|
175
|
+
if (chart && Array.isArray(chart.nodes)) {
|
|
176
|
+
return chart.nodes
|
|
177
|
+
.filter((n) => n && matchTeam(n.team))
|
|
178
|
+
.map((n) => ({
|
|
179
|
+
slug: String(n.slug),
|
|
180
|
+
displayName: n.displayName != null ? n.displayName : null,
|
|
181
|
+
role: n.role && n.role.title != null ? String(n.role.title) : null,
|
|
182
|
+
team: n.team && n.team.name != null ? String(n.team.name) : null,
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const hierarchy = ctx.hierarchy;
|
|
187
|
+
if (hierarchy && Array.isArray(hierarchy.members)) {
|
|
188
|
+
return hierarchy.members
|
|
189
|
+
.filter((m) => m && (matchTeam(m.team) || lc(m.team) === needle))
|
|
190
|
+
.map((m) => ({
|
|
191
|
+
slug: m.slug != null ? String(m.slug) : "",
|
|
192
|
+
displayName: m.displayName != null ? m.displayName : null,
|
|
193
|
+
role: m.role && m.role.title != null ? String(m.role.title) : (m.role != null ? String(m.role) : null),
|
|
194
|
+
team: m.team && m.team.name != null ? String(m.team.name) : (m.team != null ? String(m.team) : null),
|
|
195
|
+
}))
|
|
196
|
+
.filter((m) => m.slug);
|
|
197
|
+
}
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Which adopted decisions touch `topic`? Filters `ctx.decisions` by a
|
|
203
|
+
* case-insensitive substring across title / rationale / scope / summary / tags.
|
|
204
|
+
* An empty/blank topic returns ALL cached decisions. Returns `[]` when there are
|
|
205
|
+
* no decisions / no match.
|
|
206
|
+
* @param {object|string} ctxOrRoot
|
|
207
|
+
* @param {string} [topic]
|
|
208
|
+
* @returns {object[]} the matching decision records (verbatim)
|
|
209
|
+
*/
|
|
210
|
+
export function relevantDecisions(ctxOrRoot, topic) {
|
|
211
|
+
const ctx = resolveCtx(ctxOrRoot);
|
|
212
|
+
const decisions = Array.isArray(ctx.decisions) ? ctx.decisions : [];
|
|
213
|
+
const needle = lc(topic).trim();
|
|
214
|
+
if (!needle) return decisions.slice();
|
|
215
|
+
|
|
216
|
+
return decisions.filter((d) => {
|
|
217
|
+
if (!d || typeof d !== "object") return false;
|
|
218
|
+
const tags = Array.isArray(d.tags) ? d.tags.join(" ") : "";
|
|
219
|
+
const hay = `${lc(d.title)} ${lc(d.rationale)} ${lc(d.scope)} ${lc(d.summary)} ${lc(tags)}`;
|
|
220
|
+
return hay.includes(needle);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* awareness.test.mjs — pure situational-awareness helpers over a cached org
|
|
3
|
+
* context. Fixture-driven; every helper is fail-open (null / [] when data absent).
|
|
4
|
+
* Run: node --test lib/org/awareness.test.mjs
|
|
5
|
+
*/
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { mkdtempSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
import { ownerOf, escalateTo, whoReportsToMe, colleaguesInTeam, relevantDecisions } from "./awareness.mjs";
|
|
15
|
+
import { persistOrgContext } from "./bootstrap-context.mjs";
|
|
16
|
+
|
|
17
|
+
const CTX = {
|
|
18
|
+
profile: {
|
|
19
|
+
slug: "robin",
|
|
20
|
+
displayName: "Robin Vega",
|
|
21
|
+
escalationPath: "CTO, then CEO for irreversible spend",
|
|
22
|
+
supervisor: { slug: "layla", displayName: "Layla Al-Masri", role: { title: "CTO" } },
|
|
23
|
+
reportingLine: { manager: { slug: "layla", displayName: "Layla Al-Masri" } },
|
|
24
|
+
reports: [
|
|
25
|
+
{ member: { slug: "kai", displayName: "Kai Tan" }, style: "SOLID" },
|
|
26
|
+
{ member: { slug: "noa", displayName: "Noa Levi" }, style: "DOTTED" },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
chart: {
|
|
30
|
+
nodes: [
|
|
31
|
+
{ slug: "robin", displayName: "Robin Vega", role: { title: "VP Eng" }, team: { slug: "platform", name: "Platform Engineering", shortLabel: "PLT" } },
|
|
32
|
+
{ slug: "kai", displayName: "Kai Tan", role: { title: "Staff Eng" }, team: { slug: "platform", name: "Platform Engineering", shortLabel: "PLT" } },
|
|
33
|
+
{ slug: "mara", displayName: "Mara Cole", role: { title: "AE" }, team: { slug: "revenue", name: "Revenue", shortLabel: "REV" } },
|
|
34
|
+
],
|
|
35
|
+
edges: [
|
|
36
|
+
{ kind: "reporting", from: "robin", to: "kai", type: "SOLID" },
|
|
37
|
+
{ kind: "reporting", from: "robin", to: "noa", type: "DOTTED" },
|
|
38
|
+
{ kind: "reporting", from: "layla", to: "robin", type: "SOLID" },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
relationships: {
|
|
42
|
+
member: { slug: "robin" },
|
|
43
|
+
reportingLine: { manager: { slug: "layla", displayName: "Layla Al-Masri" } },
|
|
44
|
+
reports: [{ member: { slug: "kai", displayName: "Kai Tan" }, style: "SOLID" }],
|
|
45
|
+
governance: [],
|
|
46
|
+
},
|
|
47
|
+
strategy: {
|
|
48
|
+
streams: [
|
|
49
|
+
{ code: "S1", name: "Platform Reliability", mission: "Keep the lights on", raci: [
|
|
50
|
+
{ role: "Accountable", member: { slug: "robin", displayName: "Robin Vega" } },
|
|
51
|
+
{ role: "Responsible", member: { slug: "kai", displayName: "Kai Tan" } },
|
|
52
|
+
] },
|
|
53
|
+
{ code: "S2", name: "Revenue Growth", mission: "Grow ARR", raci: [
|
|
54
|
+
{ role: "Accountable", member: { slug: "mara", displayName: "Mara Cole" } },
|
|
55
|
+
] },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
hierarchy: { members: [{ slug: "robin" }, { slug: "kai" }], reporting: [{ from: "layla", to: "robin" }] },
|
|
59
|
+
decisions: [
|
|
60
|
+
{ id: "D1", title: "Adopt Postgres for the ledger", rationale: "ACID + maturity", scope: "data", status: "adopted" },
|
|
61
|
+
{ id: "D2", title: "Standardise on Slack", rationale: "comms", scope: "ops", tags: ["comms"] },
|
|
62
|
+
],
|
|
63
|
+
policy: { bundleId: "p1" },
|
|
64
|
+
board: [],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// --- ownerOf ----------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
test("ownerOf: matches a strategy stream and returns its Accountable owner", () => {
|
|
70
|
+
const o = ownerOf(CTX, "reliability");
|
|
71
|
+
assert.equal(o.owner.slug, "robin");
|
|
72
|
+
assert.equal(o.role, "Accountable");
|
|
73
|
+
assert.equal(o.stream.code, "S1");
|
|
74
|
+
const rev = ownerOf(CTX, "Revenue");
|
|
75
|
+
assert.equal(rev.owner.slug, "mara");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("ownerOf: no match / no strategy / blank scope → null", () => {
|
|
79
|
+
assert.equal(ownerOf(CTX, "nonexistent-scope"), null);
|
|
80
|
+
assert.equal(ownerOf({}, "reliability"), null);
|
|
81
|
+
assert.equal(ownerOf(CTX, ""), null);
|
|
82
|
+
assert.equal(ownerOf(null, "x"), null);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// --- escalateTo -------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
test("escalateTo: surfaces escalationPath + supervisor manager", () => {
|
|
88
|
+
const e = escalateTo(CTX);
|
|
89
|
+
assert.match(e.escalationPath, /CTO/);
|
|
90
|
+
assert.equal(e.manager.slug, "layla");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("escalateTo: falls back to reporting-line manager; null when nothing", () => {
|
|
94
|
+
const e = escalateTo({ profile: { reportingLine: { manager: { slug: "boss", displayName: "Boss" } } } });
|
|
95
|
+
assert.equal(e.escalationPath, null);
|
|
96
|
+
assert.equal(e.manager.slug, "boss");
|
|
97
|
+
assert.equal(escalateTo({}), null);
|
|
98
|
+
assert.equal(escalateTo(null), null);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// --- whoReportsToMe ---------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
test("whoReportsToMe: prefers the self profile's reports", () => {
|
|
104
|
+
const reports = whoReportsToMe(CTX, "robin");
|
|
105
|
+
assert.deepEqual(reports.map((r) => r.slug).sort(), ["kai", "noa"]);
|
|
106
|
+
assert.equal(reports.find((r) => r.slug === "noa").style, "DOTTED");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("whoReportsToMe: derives from chart edges for a non-self manager", () => {
|
|
110
|
+
// Querying for "layla" (not the profile member) → derive from chart edges.
|
|
111
|
+
const reports = whoReportsToMe(CTX, "layla");
|
|
112
|
+
assert.deepEqual(reports.map((r) => r.slug), ["robin"]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("whoReportsToMe: no data → []", () => {
|
|
116
|
+
assert.deepEqual(whoReportsToMe({}, "x"), []);
|
|
117
|
+
assert.deepEqual(whoReportsToMe(null), []);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// --- colleaguesInTeam -------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
test("colleaguesInTeam: filters chart nodes by team slug/name/shortLabel", () => {
|
|
123
|
+
assert.deepEqual(colleaguesInTeam(CTX, "platform").map((m) => m.slug).sort(), ["kai", "robin"]);
|
|
124
|
+
assert.deepEqual(colleaguesInTeam(CTX, "Platform Engineering").map((m) => m.slug).sort(), ["kai", "robin"]);
|
|
125
|
+
assert.deepEqual(colleaguesInTeam(CTX, "REV").map((m) => m.slug), ["mara"]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("colleaguesInTeam: falls back to hierarchy members; [] when absent", () => {
|
|
129
|
+
const ctx = { hierarchy: { members: [{ slug: "x", displayName: "X", team: { slug: "ops", name: "Ops" } }] } };
|
|
130
|
+
assert.deepEqual(colleaguesInTeam(ctx, "ops").map((m) => m.slug), ["x"]);
|
|
131
|
+
assert.deepEqual(colleaguesInTeam(CTX, "no-such-team"), []);
|
|
132
|
+
assert.deepEqual(colleaguesInTeam({}, "platform"), []);
|
|
133
|
+
assert.deepEqual(colleaguesInTeam(CTX, ""), []);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// --- relevantDecisions ------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
test("relevantDecisions: filters by topic across title/rationale/scope/tags", () => {
|
|
139
|
+
assert.deepEqual(relevantDecisions(CTX, "postgres").map((d) => d.id), ["D1"]);
|
|
140
|
+
assert.deepEqual(relevantDecisions(CTX, "comms").map((d) => d.id), ["D2"]);
|
|
141
|
+
assert.deepEqual(relevantDecisions(CTX, "data").map((d) => d.id), ["D1"]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("relevantDecisions: blank topic returns all; no data → []", () => {
|
|
145
|
+
assert.equal(relevantDecisions(CTX, "").length, 2);
|
|
146
|
+
assert.deepEqual(relevantDecisions(CTX, "zzz"), []);
|
|
147
|
+
assert.deepEqual(relevantDecisions({}, "x"), []);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// --- loads from disk (agentRoot string arg) ---------------------------------
|
|
151
|
+
|
|
152
|
+
test("awareness helpers accept an agentRoot string (loads config/org-context.json)", () => {
|
|
153
|
+
const root = mkdtempSync(join(tmpdir(), "maestro-aware-"));
|
|
154
|
+
mkdirSync(join(root, "config"), { recursive: true });
|
|
155
|
+
persistOrgContext(root, CTX);
|
|
156
|
+
assert.equal(ownerOf(root, "reliability").owner.slug, "robin");
|
|
157
|
+
assert.deepEqual(whoReportsToMe(root, "robin").map((r) => r.slug).sort(), ["kai", "noa"]);
|
|
158
|
+
assert.equal(escalateTo(root).manager.slug, "layla");
|
|
159
|
+
});
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/org/board.mjs — agent-side board operations + read-only YAML projection.
|
|
3
|
+
*
|
|
4
|
+
* Two layers:
|
|
5
|
+
*
|
|
6
|
+
* 1. Board ops — thin, fail-open wrappers over the Cohort board methods via
|
|
7
|
+
* lib/org/client.mjs: create / claim / complete / decompose / assign / link /
|
|
8
|
+
* ready. The server is the SoT for the board (9-state machine, CAS version,
|
|
9
|
+
* DAG links, leases); these helpers just route typed calls and never throw
|
|
10
|
+
* (a down server yields an error frame, []/null for reads).
|
|
11
|
+
*
|
|
12
|
+
* decompose/assign are governance-gated server-side (§0.4) — the helpers do
|
|
13
|
+
* not pre-check; they forward the call and surface the GOVERNANCE_NOT_READY
|
|
14
|
+
* error frame the server returns until governance_ready.
|
|
15
|
+
*
|
|
16
|
+
* 2. Projection writer — writes a READ-ONLY YAML view of the board to
|
|
17
|
+
* state/org/board/*.yaml so a human (or an offline agent) can see the board
|
|
18
|
+
* without the server (§9.1 "read-only YAML projection"). The server is still
|
|
19
|
+
* the SoT; these files carry a provenance header marking them derived +
|
|
20
|
+
* read-only, written atomically via lib/fs-atomic.mjs. One file per board
|
|
21
|
+
* plus a `ready.yaml` for the ready-frontier feed.
|
|
22
|
+
*
|
|
23
|
+
* Pure-core + injectable IO: the client is injected as `opts.client` (defaults to
|
|
24
|
+
* the real client); the projection writer takes an explicit agentRoot. Node
|
|
25
|
+
* builtins + js-yaml (already a maestro dep, dynamically imported). ESM.
|
|
26
|
+
*
|
|
27
|
+
* @module lib/org/board
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
"use strict";
|
|
31
|
+
|
|
32
|
+
import { join } from "node:path";
|
|
33
|
+
|
|
34
|
+
import { writeFileAtomic } from "../fs-atomic.mjs";
|
|
35
|
+
import * as client from "./client.mjs";
|
|
36
|
+
import { BOARD_STATUS } from "./protocol.mjs";
|
|
37
|
+
|
|
38
|
+
/** The Cohort client surface this module needs (injectable for tests). */
|
|
39
|
+
function resolveClient(opts) {
|
|
40
|
+
return (opts && opts.client) || client;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Connection options for a client RPC, distilled from opts. */
|
|
44
|
+
function rpcOpts(opts) {
|
|
45
|
+
return {
|
|
46
|
+
base: opts && opts.base,
|
|
47
|
+
token: opts && opts.token,
|
|
48
|
+
fetchImpl: opts && opts.fetchImpl,
|
|
49
|
+
idempotencyKey: opts && opts.idempotencyKey,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Board ops (over the Cohort client)
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a board work-item (board.create). Idempotent server-side on the item id.
|
|
59
|
+
* @param {object} item - { id?, title, status?, priority?, ... }
|
|
60
|
+
* @param {object} [opts] - { client?, base, token, fetchImpl?, idempotencyKey? }
|
|
61
|
+
* @returns {Promise<object>} res frame
|
|
62
|
+
*/
|
|
63
|
+
export function createItem(item, opts = {}) {
|
|
64
|
+
return resolveClient(opts).createItem(item || {}, rpcOpts(opts));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Claim a ready board item (board.claim). The server resolves the race + lease
|
|
69
|
+
* atomically; the loser gets a LEASE_HELD/CONFLICT error frame.
|
|
70
|
+
* @param {object} params - { itemId, ... }
|
|
71
|
+
* @param {object} [opts]
|
|
72
|
+
* @returns {Promise<object>} res frame
|
|
73
|
+
*/
|
|
74
|
+
export function claimItem(params, opts = {}) {
|
|
75
|
+
return resolveClient(opts).claimItem(params || {}, rpcOpts(opts));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Complete a board item (board.complete). The server enforces the completion
|
|
80
|
+
* gate (lease token + expected_run_id CAS + proof).
|
|
81
|
+
* @param {object} params - { itemId, proof?, ... }
|
|
82
|
+
* @param {object} [opts]
|
|
83
|
+
* @returns {Promise<object>} res frame
|
|
84
|
+
*/
|
|
85
|
+
export function completeItem(params, opts = {}) {
|
|
86
|
+
return resolveClient(opts).completeItem(params || {}, rpcOpts(opts));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Decompose an item into children + blocking links (board.decompose).
|
|
91
|
+
* GOVERNANCE-GATED server-side.
|
|
92
|
+
* @param {object} params - { itemId, children:[{title,…}], linkType? }
|
|
93
|
+
* @param {object} [opts]
|
|
94
|
+
* @returns {Promise<object>} res frame
|
|
95
|
+
*/
|
|
96
|
+
export function decompose(params, opts = {}) {
|
|
97
|
+
return resolveClient(opts).boardDecompose(params || {}, rpcOpts(opts));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Assign an owner to an item (board.assign). GOVERNANCE-GATED server-side.
|
|
102
|
+
* @param {object} params - { itemId, assignee }
|
|
103
|
+
* @param {object} [opts]
|
|
104
|
+
* @returns {Promise<object>} res frame
|
|
105
|
+
*/
|
|
106
|
+
export function assign(params, opts = {}) {
|
|
107
|
+
return resolveClient(opts).boardAssign(params || {}, rpcOpts(opts));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Link two items with a typed DAG edge (board.link). The server runs the DFS
|
|
112
|
+
* cycle check on insert. NOT governance-gated.
|
|
113
|
+
* @param {object} params - { fromItem, toItem, linkType? }
|
|
114
|
+
* @param {object} [opts]
|
|
115
|
+
* @returns {Promise<object>} res frame
|
|
116
|
+
*/
|
|
117
|
+
export function link(params, opts = {}) {
|
|
118
|
+
return resolveClient(opts).boardLink(params || {}, rpcOpts(opts));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Read the ready-frontier feed (GET /v1/board.ready). Returns the items array,
|
|
123
|
+
* or [] on any failure (fail-open).
|
|
124
|
+
* @param {object} [opts] - { client?, base, token, fetchImpl? }
|
|
125
|
+
* @returns {Promise<object[]>}
|
|
126
|
+
*/
|
|
127
|
+
export function ready(opts = {}) {
|
|
128
|
+
return resolveClient(opts).listReady(rpcOpts(opts));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Read-only YAML projection writer
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
/** Header stamped on every projection file: derived, read-only, server is SoT. */
|
|
136
|
+
const PROJECTION_HEADER =
|
|
137
|
+
"# DERIVED, READ-ONLY board projection written by lib/org/board.mjs.\n" +
|
|
138
|
+
"# The Cohort org server is the source of truth — DO NOT hand-edit this file;\n" +
|
|
139
|
+
"# local edits are overwritten on the next sync. For a human/agent to see the\n" +
|
|
140
|
+
"# board without the server (SPEC-org-server §9.1).\n";
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Build the projection document object for a set of board items. Pure: groups by
|
|
144
|
+
* status (in the canonical BOARD_STATUS order), stamps generatedAt + counts, and
|
|
145
|
+
* normalises each item to a stable, lease-token-free projection (lease tokens are
|
|
146
|
+
* never written to any read surface — §8.8). Unknown statuses bucket under
|
|
147
|
+
* "_other" so a forward-compatible server status can't drop items silently.
|
|
148
|
+
*
|
|
149
|
+
* @param {object[]} items
|
|
150
|
+
* @param {object} [o] - { board?, generatedAt?, cursorHead? }
|
|
151
|
+
* @returns {{schemaVersion:number, board:string, generatedAt:string, count:number, byStatus:object, items:object[]}}
|
|
152
|
+
*/
|
|
153
|
+
export function buildProjection(items, o = {}) {
|
|
154
|
+
const list = Array.isArray(items) ? items : [];
|
|
155
|
+
const projected = list.map(projectItem);
|
|
156
|
+
const byStatus = {};
|
|
157
|
+
for (const status of BOARD_STATUS) byStatus[status] = 0;
|
|
158
|
+
for (const it of projected) {
|
|
159
|
+
const s = BOARD_STATUS.includes(it.status) ? it.status : "_other";
|
|
160
|
+
byStatus[s] = (byStatus[s] || 0) + 1;
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
schemaVersion: 1,
|
|
164
|
+
board: o.board ? String(o.board) : "self",
|
|
165
|
+
generatedAt: o.generatedAt || new Date().toISOString(),
|
|
166
|
+
cursorHead: Number.isFinite(o.cursorHead) ? o.cursorHead : undefined,
|
|
167
|
+
count: projected.length,
|
|
168
|
+
byStatus,
|
|
169
|
+
items: projected,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Normalise one server item to the read-only projection shape (token-free). Pure. */
|
|
174
|
+
function projectItem(it) {
|
|
175
|
+
if (!it || typeof it !== "object") return { id: "", title: "", status: "" };
|
|
176
|
+
const out = {
|
|
177
|
+
id: it.id || it.itemId || it.item_id || "",
|
|
178
|
+
title: it.title || "",
|
|
179
|
+
status: it.status || "",
|
|
180
|
+
priority: it.priority || undefined,
|
|
181
|
+
assignee: it.assignee || it.assignee_agent || it.owner || undefined,
|
|
182
|
+
blockedBy: Array.isArray(it.blockedBy) ? it.blockedBy.slice() : undefined,
|
|
183
|
+
updatedAt: it.updatedAt || it.updated_at || undefined,
|
|
184
|
+
};
|
|
185
|
+
// Defence-in-depth: never leak a lease/token field to a read surface (§8.8).
|
|
186
|
+
for (const k of Object.keys(out)) if (out[k] === undefined) delete out[k];
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Write a read-only YAML projection of `items` to
|
|
192
|
+
* state/org/board/<board>.yaml. Atomic (lib/fs-atomic). Returns the path written.
|
|
193
|
+
* Fail-open: a serialisation/IO error returns null rather than throwing.
|
|
194
|
+
*
|
|
195
|
+
* @param {string} agentRoot absolute path to the agent instance root
|
|
196
|
+
* @param {object[]} items board items (e.g. from board.ready or a snapshot)
|
|
197
|
+
* @param {object} [o] - { board?, generatedAt?, cursorHead? }
|
|
198
|
+
* @returns {Promise<string|null>} the file path written, or null on failure
|
|
199
|
+
*/
|
|
200
|
+
export async function writeProjection(agentRoot, items, o = {}) {
|
|
201
|
+
if (!agentRoot) return null;
|
|
202
|
+
try {
|
|
203
|
+
const m = await import("js-yaml");
|
|
204
|
+
const yaml = m.default || m;
|
|
205
|
+
const doc = buildProjection(items, o);
|
|
206
|
+
const body = PROJECTION_HEADER + yaml.dump(doc, { lineWidth: 120, sortKeys: false });
|
|
207
|
+
const board = doc.board.replace(/[^a-zA-Z0-9_.-]/g, "_") || "self";
|
|
208
|
+
const path = join(String(agentRoot), "state", "org", "board", `${board}.yaml`);
|
|
209
|
+
writeFileAtomic(path, body);
|
|
210
|
+
return path;
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Refresh the ready-frontier projection: fetch board.ready from the server and
|
|
218
|
+
* write it as state/org/board/ready.yaml. Convenience for the org-sync cadence.
|
|
219
|
+
* Fail-open: a down server writes an EMPTY ready projection (so a stale ready
|
|
220
|
+
* feed never lingers), and returns the path.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} agentRoot
|
|
223
|
+
* @param {object} [opts] - { client?, base, token, fetchImpl?, generatedAt? }
|
|
224
|
+
* @returns {Promise<string|null>}
|
|
225
|
+
*/
|
|
226
|
+
export async function writeReadyProjection(agentRoot, opts = {}) {
|
|
227
|
+
const items = await ready(opts);
|
|
228
|
+
return writeProjection(agentRoot, items, { board: "ready", generatedAt: opts.generatedAt });
|
|
229
|
+
}
|