@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,442 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# test-rag-phase2.sh — Tests for RAG Phase 2 (SQLite FTS5 backend)
|
|
5
|
+
#
|
|
6
|
+
# Tests:
|
|
7
|
+
# 1-3. Indexer: full index, stats, incremental re-index
|
|
8
|
+
# 4-5. SQLite search: CEO gets results, correct backend label
|
|
9
|
+
# 6-7. Ring-fencing: leadership cannot see CEO private DMs
|
|
10
|
+
# 8-9. Ring-fencing: unknown user sees only public scope
|
|
11
|
+
# 10. Ring-fencing: partner sees public + own data only
|
|
12
|
+
# 11. Fallback: sqlite backend falls back to grep when DB missing
|
|
13
|
+
# 12. User resolution works with sqlite backend
|
|
14
|
+
# 13. Text format works with sqlite backend
|
|
15
|
+
# 14. FTS5 search quality: multi-word queries return relevant results
|
|
16
|
+
# 15. Incremental indexing skips unchanged files
|
|
17
|
+
# 16. Phase 1 backward compatibility: grep backend still works
|
|
18
|
+
# 17. Ring-fencing DB-level: direct SQL confirms no scope leakage
|
|
19
|
+
# 18. Indexer --stats runs without error
|
|
20
|
+
# 19. Custom --db path works
|
|
21
|
+
# 20. Empty query handling
|
|
22
|
+
#
|
|
23
|
+
# Usage: ./scripts/test-rag-phase2.sh
|
|
24
|
+
|
|
25
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
26
|
+
BASE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
27
|
+
SEARCH_SCRIPT="${SCRIPT_DIR}/user-context-search.py"
|
|
28
|
+
INDEXER_SCRIPT="${SCRIPT_DIR}/rag-indexer.py"
|
|
29
|
+
DB_PATH="${BASE_DIR}/state/rag/search.db"
|
|
30
|
+
TEST_DB="/tmp/test-rag-phase2.db"
|
|
31
|
+
PASS=0
|
|
32
|
+
FAIL=0
|
|
33
|
+
TOTAL=0
|
|
34
|
+
|
|
35
|
+
# ── Helpers ────────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
pass_test() {
|
|
38
|
+
PASS=$((PASS + 1))
|
|
39
|
+
TOTAL=$((TOTAL + 1))
|
|
40
|
+
echo " PASS: $1"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fail_test() {
|
|
44
|
+
FAIL=$((FAIL + 1))
|
|
45
|
+
TOTAL=$((TOTAL + 1))
|
|
46
|
+
echo " FAIL: $1"
|
|
47
|
+
if [ -n "${2:-}" ]; then
|
|
48
|
+
echo " Detail: $2"
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
run_search() {
|
|
53
|
+
python3 "$SEARCH_SCRIPT" "$@" 2>/dev/null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
run_indexer() {
|
|
57
|
+
python3 "$INDEXER_SCRIPT" "$@" 2>/dev/null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# ── Setup ──────────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
echo "================================================================"
|
|
63
|
+
echo "RAG Phase 2 — SQLite FTS5 backend test suite"
|
|
64
|
+
echo "================================================================"
|
|
65
|
+
echo ""
|
|
66
|
+
|
|
67
|
+
# Clean up test DB if it exists
|
|
68
|
+
rm -f "$TEST_DB"
|
|
69
|
+
|
|
70
|
+
# ── Test 1: Full index builds database ────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
echo "Test 1: Full index builds database"
|
|
73
|
+
OUTPUT=$(run_indexer --full --db "$TEST_DB" 2>&1 || true)
|
|
74
|
+
|
|
75
|
+
if [ -f "$TEST_DB" ]; then
|
|
76
|
+
pass_test "Database file created at $TEST_DB"
|
|
77
|
+
else
|
|
78
|
+
fail_test "Database file not created" "$OUTPUT"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if echo "$OUTPUT" | grep -q "Indexed.*files"; then
|
|
82
|
+
pass_test "Indexer reported indexed files"
|
|
83
|
+
else
|
|
84
|
+
fail_test "Indexer should report indexed file count" "$OUTPUT"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
echo ""
|
|
88
|
+
|
|
89
|
+
# ── Test 2: Database has documents ────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
echo "Test 2: Database contains documents"
|
|
92
|
+
DOC_COUNT=$(python3 -c "
|
|
93
|
+
import sqlite3
|
|
94
|
+
conn = sqlite3.connect('$TEST_DB')
|
|
95
|
+
count = conn.execute('SELECT COUNT(*) FROM documents').fetchone()[0]
|
|
96
|
+
print(count)
|
|
97
|
+
conn.close()
|
|
98
|
+
" 2>/dev/null || echo "0")
|
|
99
|
+
|
|
100
|
+
if [ "$DOC_COUNT" -gt 0 ] 2>/dev/null; then
|
|
101
|
+
pass_test "Database contains $DOC_COUNT documents"
|
|
102
|
+
else
|
|
103
|
+
fail_test "Database should contain documents (got $DOC_COUNT)"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
echo ""
|
|
107
|
+
|
|
108
|
+
# ── Test 3: FTS5 virtual table exists ─────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
echo "Test 3: FTS5 virtual table exists and is searchable"
|
|
111
|
+
FTS_RESULT=$(python3 -c "
|
|
112
|
+
import sqlite3
|
|
113
|
+
conn = sqlite3.connect('$TEST_DB')
|
|
114
|
+
try:
|
|
115
|
+
rows = conn.execute(\"SELECT COUNT(*) FROM documents_fts WHERE documents_fts MATCH '\\\"northwind\\\"'\").fetchone()[0]
|
|
116
|
+
print(rows)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f'ERROR: {e}')
|
|
119
|
+
conn.close()
|
|
120
|
+
" 2>/dev/null || echo "ERROR")
|
|
121
|
+
|
|
122
|
+
if echo "$FTS_RESULT" | grep -qv "ERROR"; then
|
|
123
|
+
if [ "$FTS_RESULT" -gt 0 ] 2>/dev/null; then
|
|
124
|
+
pass_test "FTS5 table is searchable, found $FTS_RESULT matches for 'northwind'"
|
|
125
|
+
else
|
|
126
|
+
fail_test "FTS5 search returned 0 results for 'northwind'"
|
|
127
|
+
fi
|
|
128
|
+
else
|
|
129
|
+
fail_test "FTS5 table query failed: $FTS_RESULT"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
echo ""
|
|
133
|
+
|
|
134
|
+
# ── Test 4: SQLite backend — CEO search returns results ──────────────────────
|
|
135
|
+
|
|
136
|
+
echo "Test 4: SQLite backend — CEO search returns results"
|
|
137
|
+
RESULT=$(run_search --user alex --query "the regulator" --backend sqlite --db "$TEST_DB" --format json 2>&1 || true)
|
|
138
|
+
|
|
139
|
+
MATCH_COUNT=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result_count'])" 2>/dev/null || echo "0")
|
|
140
|
+
if [ "$MATCH_COUNT" -gt 0 ] 2>/dev/null; then
|
|
141
|
+
pass_test "CEO sqlite search returned $MATCH_COUNT results for 'the regulator'"
|
|
142
|
+
else
|
|
143
|
+
fail_test "CEO sqlite search should return results for 'the regulator'"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
echo ""
|
|
147
|
+
|
|
148
|
+
# ── Test 5: SQLite backend label in output ────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
echo "Test 5: Backend label is 'sqlite' in JSON output"
|
|
151
|
+
BACKEND=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('backend',''))" 2>/dev/null || echo "")
|
|
152
|
+
if [ "$BACKEND" = "sqlite" ]; then
|
|
153
|
+
pass_test "Backend reported as 'sqlite'"
|
|
154
|
+
else
|
|
155
|
+
fail_test "Backend should be 'sqlite', got '$BACKEND'"
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo ""
|
|
159
|
+
|
|
160
|
+
# ── Test 6: Ring-fencing — leadership cannot see CEO private DMs ──────────────
|
|
161
|
+
|
|
162
|
+
echo "Test 6: Ring-fencing — leadership cannot see CEO private DMs"
|
|
163
|
+
RESULT=$(run_search --user jordan --query "alex" --backend sqlite --db "$TEST_DB" --format json --max-results 50 2>&1 || true)
|
|
164
|
+
|
|
165
|
+
LEAKED=$(echo "$RESULT" | python3 -c "
|
|
166
|
+
import sys, json
|
|
167
|
+
d = json.load(sys.stdin)
|
|
168
|
+
# Check if any result has user_scope of a private user (alex-chen)
|
|
169
|
+
leaked = [m for m in d['matches'] if m.get('user_scope') == 'alex-chen']
|
|
170
|
+
print(len(leaked))
|
|
171
|
+
" 2>/dev/null || echo "-1")
|
|
172
|
+
|
|
173
|
+
if [ "$LEAKED" = "0" ]; then
|
|
174
|
+
pass_test "Leadership user sees 0 of Alex's private documents"
|
|
175
|
+
else
|
|
176
|
+
fail_test "Leadership user should not see Alex's private docs (saw $LEAKED)"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
echo ""
|
|
180
|
+
|
|
181
|
+
# ── Test 7: Ring-fencing — CEO can see private DMs ────────────────────────────
|
|
182
|
+
|
|
183
|
+
echo "Test 7: Ring-fencing — CEO can see private DMs"
|
|
184
|
+
RESULT=$(run_search --user alex --query "voice" --backend sqlite --db "$TEST_DB" --format json --max-results 50 2>&1 || true)
|
|
185
|
+
|
|
186
|
+
PRIVATE_COUNT=$(echo "$RESULT" | python3 -c "
|
|
187
|
+
import sys, json
|
|
188
|
+
d = json.load(sys.stdin)
|
|
189
|
+
private = [m for m in d['matches'] if m.get('user_scope') == 'alex-chen']
|
|
190
|
+
print(len(private))
|
|
191
|
+
" 2>/dev/null || echo "0")
|
|
192
|
+
|
|
193
|
+
if [ "$PRIVATE_COUNT" -gt 0 ] 2>/dev/null; then
|
|
194
|
+
pass_test "CEO can see $PRIVATE_COUNT private documents"
|
|
195
|
+
else
|
|
196
|
+
fail_test "CEO should be able to see private DM documents"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
echo ""
|
|
200
|
+
|
|
201
|
+
# ── Test 8: Ring-fencing — unknown user sees only public ─────────────────────
|
|
202
|
+
|
|
203
|
+
echo "Test 8: Ring-fencing — unknown user sees only public scope"
|
|
204
|
+
RESULT=$(run_search --user nobody --query "northwind" --backend sqlite --db "$TEST_DB" --format json --max-results 50 2>&1 || true)
|
|
205
|
+
|
|
206
|
+
NON_PUBLIC=$(echo "$RESULT" | python3 -c "
|
|
207
|
+
import sys, json
|
|
208
|
+
d = json.load(sys.stdin)
|
|
209
|
+
non_pub = [m for m in d['matches'] if m.get('user_scope') != 'public']
|
|
210
|
+
print(len(non_pub))
|
|
211
|
+
" 2>/dev/null || echo "-1")
|
|
212
|
+
|
|
213
|
+
if [ "$NON_PUBLIC" = "0" ]; then
|
|
214
|
+
pass_test "Unknown user sees only public-scoped documents"
|
|
215
|
+
else
|
|
216
|
+
fail_test "Unknown user should only see public docs (saw $NON_PUBLIC non-public)"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
echo ""
|
|
220
|
+
|
|
221
|
+
# ── Test 9: Ring-fencing — unknown user access level is default ──────────────
|
|
222
|
+
|
|
223
|
+
echo "Test 9: Unknown user has 'default' access level"
|
|
224
|
+
ACCESS=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['access_level'])" 2>/dev/null || echo "")
|
|
225
|
+
if [ "$ACCESS" = "default" ]; then
|
|
226
|
+
pass_test "Unknown user access_level is 'default'"
|
|
227
|
+
else
|
|
228
|
+
fail_test "Unknown user access_level should be 'default', got '$ACCESS'"
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
echo ""
|
|
232
|
+
|
|
233
|
+
# ── Test 10: Ring-fencing — partner sees public + own only ───────────────────
|
|
234
|
+
|
|
235
|
+
echo "Test 10: Ring-fencing — partner sees public + own data only"
|
|
236
|
+
RESULT=$(run_search --user shayan --query "northwind" --backend sqlite --db "$TEST_DB" --format json --max-results 50 2>&1 || true)
|
|
237
|
+
|
|
238
|
+
BAD_SCOPE=$(echo "$RESULT" | python3 -c "
|
|
239
|
+
import sys, json
|
|
240
|
+
d = json.load(sys.stdin)
|
|
241
|
+
allowed = {'public', 'casey-morgan'}
|
|
242
|
+
bad = [m for m in d['matches'] if m.get('user_scope') not in allowed]
|
|
243
|
+
print(len(bad))
|
|
244
|
+
" 2>/dev/null || echo "-1")
|
|
245
|
+
|
|
246
|
+
if [ "$BAD_SCOPE" = "0" ]; then
|
|
247
|
+
pass_test "Partner sees only public + own-scope documents"
|
|
248
|
+
else
|
|
249
|
+
fail_test "Partner should only see public + own scope (saw $BAD_SCOPE violations)"
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
echo ""
|
|
253
|
+
|
|
254
|
+
# ── Test 11: Fallback — sqlite falls back to grep when DB missing ────────────
|
|
255
|
+
|
|
256
|
+
echo "Test 11: Fallback to grep when SQLite DB missing"
|
|
257
|
+
RESULT=$(python3 "$SEARCH_SCRIPT" --user alex --query "the regulator" --backend sqlite --db /tmp/nonexistent-db-test.db --format json 2>/dev/null || true)
|
|
258
|
+
|
|
259
|
+
BACKEND=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('backend',''))" 2>/dev/null || echo "")
|
|
260
|
+
if [ "$BACKEND" = "grep" ]; then
|
|
261
|
+
pass_test "Fell back to 'grep' backend when DB missing"
|
|
262
|
+
else
|
|
263
|
+
fail_test "Should fall back to 'grep' when DB missing, got '$BACKEND'"
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
echo ""
|
|
267
|
+
|
|
268
|
+
# ── Test 12: User resolution works with sqlite backend ───────────────────────
|
|
269
|
+
|
|
270
|
+
echo "Test 12: Slack ID resolution works with sqlite backend"
|
|
271
|
+
RESULT=$(run_search --user U099N1JGKRQ --query "test" --backend sqlite --db "$TEST_DB" --format json 2>&1 || true)
|
|
272
|
+
|
|
273
|
+
USER=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['user'])" 2>/dev/null || echo "")
|
|
274
|
+
if [ "$USER" = "alex" ]; then
|
|
275
|
+
pass_test "Slack ID U099N1JGKRQ resolved to 'alex' with sqlite backend"
|
|
276
|
+
else
|
|
277
|
+
fail_test "Slack ID should resolve to 'alex', got '$USER'"
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
echo ""
|
|
281
|
+
|
|
282
|
+
# ── Test 13: Text format works with sqlite backend ───────────────────────────
|
|
283
|
+
|
|
284
|
+
echo "Test 13: Text format output with sqlite backend"
|
|
285
|
+
RESULT=$(run_search --user alex --query "the regulator" --backend sqlite --db "$TEST_DB" --format text 2>&1 || true)
|
|
286
|
+
|
|
287
|
+
if echo "$RESULT" | grep -q "Access: ceo"; then
|
|
288
|
+
pass_test "Text format includes 'Access: ceo'"
|
|
289
|
+
else
|
|
290
|
+
fail_test "Text format should include 'Access: ceo'"
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
if echo "$RESULT" | grep -q 'Search: "the regulator"'; then
|
|
294
|
+
pass_test "Text format includes search query"
|
|
295
|
+
else
|
|
296
|
+
fail_test "Text format should include search query"
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
echo ""
|
|
300
|
+
|
|
301
|
+
# ── Test 14: FTS5 multi-word query ────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
echo "Test 14: FTS5 multi-word query"
|
|
304
|
+
RESULT=$(run_search --user alex --query "the regulator compliance" --backend sqlite --db "$TEST_DB" --format json 2>&1 || true)
|
|
305
|
+
|
|
306
|
+
MATCH_COUNT=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result_count'])" 2>/dev/null || echo "0")
|
|
307
|
+
if [ "$MATCH_COUNT" -gt 0 ] 2>/dev/null; then
|
|
308
|
+
pass_test "Multi-word query 'the regulator compliance' returned $MATCH_COUNT results"
|
|
309
|
+
else
|
|
310
|
+
fail_test "Multi-word query should return results"
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
echo ""
|
|
314
|
+
|
|
315
|
+
# ── Test 15: Incremental indexing skips unchanged files ──────────────────────
|
|
316
|
+
|
|
317
|
+
echo "Test 15: Incremental re-index skips unchanged files"
|
|
318
|
+
OUTPUT=$(run_indexer --db "$TEST_DB" 2>&1 || true)
|
|
319
|
+
|
|
320
|
+
if echo "$OUTPUT" | grep -q "Skipped.*unchanged"; then
|
|
321
|
+
pass_test "Incremental index skipped unchanged files"
|
|
322
|
+
else
|
|
323
|
+
fail_test "Incremental index should report skipped files" "$OUTPUT"
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
echo ""
|
|
327
|
+
|
|
328
|
+
# ── Test 16: Backward compatibility — grep backend works ────────────────────
|
|
329
|
+
|
|
330
|
+
echo "Test 16: Backward compatibility — grep backend still works"
|
|
331
|
+
RESULT=$(run_search --user alex --query "the regulator" --backend grep --format json 2>&1 || true)
|
|
332
|
+
|
|
333
|
+
BACKEND=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('backend',''))" 2>/dev/null || echo "")
|
|
334
|
+
if [ "$BACKEND" = "grep" ]; then
|
|
335
|
+
pass_test "Grep backend returns backend='grep'"
|
|
336
|
+
else
|
|
337
|
+
fail_test "Grep backend should return backend='grep', got '$BACKEND'"
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
MATCH_COUNT=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result_count'])" 2>/dev/null || echo "0")
|
|
341
|
+
if [ "$MATCH_COUNT" -gt 0 ] 2>/dev/null; then
|
|
342
|
+
pass_test "Grep backend returned $MATCH_COUNT results"
|
|
343
|
+
else
|
|
344
|
+
fail_test "Grep backend should return results"
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
echo ""
|
|
348
|
+
|
|
349
|
+
# ── Test 17: Direct SQL scope verification ───────────────────────────────────
|
|
350
|
+
|
|
351
|
+
echo "Test 17: Direct SQL — no scope leakage possible for leadership query"
|
|
352
|
+
LEAKED_SQL=$(python3 -c "
|
|
353
|
+
import sqlite3
|
|
354
|
+
conn = sqlite3.connect('$TEST_DB')
|
|
355
|
+
# Simulate what the search does for leadership: query with scope filter
|
|
356
|
+
allowed = ('public', 'leadership', 'sam-rivera')
|
|
357
|
+
placeholders = ','.join(['?'] * len(allowed))
|
|
358
|
+
sql = f'''SELECT COUNT(*) FROM documents WHERE user_scope NOT IN ({placeholders})'''
|
|
359
|
+
total_private = conn.execute(sql, allowed).fetchone()[0]
|
|
360
|
+
# There should be private docs in the DB that leadership can't see
|
|
361
|
+
total_all = conn.execute('SELECT COUNT(*) FROM documents').fetchone()[0]
|
|
362
|
+
print(f'{total_private},{total_all}')
|
|
363
|
+
conn.close()
|
|
364
|
+
" 2>/dev/null || echo "0,0")
|
|
365
|
+
|
|
366
|
+
PRIVATE=$(echo "$LEAKED_SQL" | cut -d',' -f1)
|
|
367
|
+
TOTAL_DOCS=$(echo "$LEAKED_SQL" | cut -d',' -f2)
|
|
368
|
+
if [ "$PRIVATE" -gt 0 ] 2>/dev/null && [ "$TOTAL_DOCS" -gt "$PRIVATE" ] 2>/dev/null; then
|
|
369
|
+
pass_test "DB has $PRIVATE private docs out of $TOTAL_DOCS total — scope filter would block them"
|
|
370
|
+
else
|
|
371
|
+
fail_test "Expected some private docs that leadership can't see (private=$PRIVATE, total=$TOTAL_DOCS)"
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
echo ""
|
|
375
|
+
|
|
376
|
+
# ── Test 18: Indexer --stats runs without error ──────────────────────────────
|
|
377
|
+
|
|
378
|
+
echo "Test 18: Indexer --stats runs without error"
|
|
379
|
+
STATS_OUTPUT=$(run_indexer --stats --db "$TEST_DB" 2>&1 || true)
|
|
380
|
+
|
|
381
|
+
if echo "$STATS_OUTPUT" | grep -q "Total documents:"; then
|
|
382
|
+
pass_test "Stats output includes 'Total documents:'"
|
|
383
|
+
else
|
|
384
|
+
fail_test "Stats should include 'Total documents:'" "$STATS_OUTPUT"
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
echo ""
|
|
388
|
+
|
|
389
|
+
# ── Test 19: Custom --db path works ──────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
echo "Test 19: Custom --db path works for search"
|
|
392
|
+
CUSTOM_DB="/tmp/test-rag-custom.db"
|
|
393
|
+
rm -f "$CUSTOM_DB"
|
|
394
|
+
run_indexer --full --db "$CUSTOM_DB" >/dev/null 2>&1 || true
|
|
395
|
+
|
|
396
|
+
if [ -f "$CUSTOM_DB" ]; then
|
|
397
|
+
RESULT=$(run_search --user alex --query "test" --backend sqlite --db "$CUSTOM_DB" --format json 2>&1 || true)
|
|
398
|
+
BACKEND=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('backend',''))" 2>/dev/null || echo "")
|
|
399
|
+
if [ "$BACKEND" = "sqlite" ]; then
|
|
400
|
+
pass_test "Custom DB path works for both indexer and search"
|
|
401
|
+
else
|
|
402
|
+
fail_test "Custom DB path search should use sqlite backend"
|
|
403
|
+
fi
|
|
404
|
+
rm -f "$CUSTOM_DB"
|
|
405
|
+
else
|
|
406
|
+
fail_test "Custom DB was not created"
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
echo ""
|
|
410
|
+
|
|
411
|
+
# ── Test 20: Empty/unusual query handling ────────────────────────────────────
|
|
412
|
+
|
|
413
|
+
echo "Test 20: Unusual query handling"
|
|
414
|
+
# Single character query
|
|
415
|
+
RESULT=$(run_search --user alex --query "x" --backend sqlite --db "$TEST_DB" --format json 2>&1 || true)
|
|
416
|
+
ACCESS=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['access_level'])" 2>/dev/null || echo "")
|
|
417
|
+
if [ "$ACCESS" = "ceo" ]; then
|
|
418
|
+
pass_test "Single-character query doesn't crash, returns valid JSON"
|
|
419
|
+
else
|
|
420
|
+
fail_test "Single-character query should return valid JSON with access_level"
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
echo ""
|
|
424
|
+
|
|
425
|
+
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
|
426
|
+
|
|
427
|
+
rm -f "$TEST_DB"
|
|
428
|
+
rm -f /tmp/test-rag-custom.db
|
|
429
|
+
|
|
430
|
+
# ── Summary ───────────────────────────────────────────────────────────────────
|
|
431
|
+
|
|
432
|
+
echo "================================================================"
|
|
433
|
+
echo "Results: $PASS passed, $FAIL failed, $TOTAL total"
|
|
434
|
+
echo "================================================================"
|
|
435
|
+
|
|
436
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
437
|
+
exit 1
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
echo ""
|
|
441
|
+
echo "All tests passed."
|
|
442
|
+
exit 0
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# test-rag-search.sh — Quick smoke tests for user-context-search.py (Phase 1 RAG)
|
|
5
|
+
#
|
|
6
|
+
# Tests:
|
|
7
|
+
# 1. CEO (alex) can search and gets broad results
|
|
8
|
+
# 2. Leadership (jordan) gets scoped results (no private CEO data)
|
|
9
|
+
# 3. Unknown user gets minimal access (knowledge/sources only)
|
|
10
|
+
# 4. Slack ID resolution works
|
|
11
|
+
# 5. Email resolution works
|
|
12
|
+
#
|
|
13
|
+
# Usage: ./scripts/test-rag-search.sh
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
16
|
+
SEARCH_SCRIPT="${SCRIPT_DIR}/user-context-search.py"
|
|
17
|
+
PASS=0
|
|
18
|
+
FAIL=0
|
|
19
|
+
TOTAL=0
|
|
20
|
+
|
|
21
|
+
# ── Helpers ────────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
pass_test() {
|
|
24
|
+
PASS=$((PASS + 1))
|
|
25
|
+
TOTAL=$((TOTAL + 1))
|
|
26
|
+
echo " PASS: $1"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fail_test() {
|
|
30
|
+
FAIL=$((FAIL + 1))
|
|
31
|
+
TOTAL=$((TOTAL + 1))
|
|
32
|
+
echo " FAIL: $1"
|
|
33
|
+
if [ -n "${2:-}" ]; then
|
|
34
|
+
echo " Detail: $2"
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
run_search() {
|
|
39
|
+
python3 "$SEARCH_SCRIPT" "$@" 2>/dev/null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# ── Tests ──────────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
echo "================================================================"
|
|
45
|
+
echo "RAG Phase 1 — user-context-search.py test suite"
|
|
46
|
+
echo "================================================================"
|
|
47
|
+
echo ""
|
|
48
|
+
|
|
49
|
+
# --- Test 1: CEO (alex) search ---
|
|
50
|
+
echo "Test 1: CEO search (alex, query='the regulator')"
|
|
51
|
+
RESULT=$(run_search --user alex --query "the regulator" --format json 2>&1 || true)
|
|
52
|
+
|
|
53
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['access_level']=='ceo'" 2>/dev/null; then
|
|
54
|
+
pass_test "access_level is 'ceo'"
|
|
55
|
+
else
|
|
56
|
+
fail_test "access_level should be 'ceo'" "$RESULT"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['user']=='alex'" 2>/dev/null; then
|
|
60
|
+
pass_test "user resolved to 'alex'"
|
|
61
|
+
else
|
|
62
|
+
fail_test "user should resolve to 'alex'"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
MATCH_COUNT=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result_count'])" 2>/dev/null || echo "0")
|
|
66
|
+
if [ "$MATCH_COUNT" -gt 0 ] 2>/dev/null; then
|
|
67
|
+
pass_test "CEO search returned $MATCH_COUNT results (>0)"
|
|
68
|
+
else
|
|
69
|
+
fail_test "CEO search returned 0 results — expected matches for 'the regulator'"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Check CEO can search in memory/ and state/ (broad access)
|
|
73
|
+
PATHS_SEARCHED=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(' '.join(d['paths_searched']))" 2>/dev/null || echo "")
|
|
74
|
+
if echo "$PATHS_SEARCHED" | grep -q "memory/"; then
|
|
75
|
+
pass_test "CEO search includes memory/ path"
|
|
76
|
+
else
|
|
77
|
+
fail_test "CEO search should include memory/ path"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if echo "$PATHS_SEARCHED" | grep -q "state/"; then
|
|
81
|
+
pass_test "CEO search includes state/ path"
|
|
82
|
+
else
|
|
83
|
+
fail_test "CEO search should include state/ path"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
echo ""
|
|
87
|
+
|
|
88
|
+
# --- Test 2: Leadership (jordan) scoped search ---
|
|
89
|
+
echo "Test 2: Leadership search (jordan, query='northwind')"
|
|
90
|
+
RESULT=$(run_search --user jordan --query "northwind" --format json 2>&1 || true)
|
|
91
|
+
|
|
92
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['access_level']=='leadership'" 2>/dev/null; then
|
|
93
|
+
pass_test "access_level is 'leadership'"
|
|
94
|
+
else
|
|
95
|
+
fail_test "access_level should be 'leadership'"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Leadership should NOT search all of memory/ or state/ or logs/
|
|
99
|
+
if echo "$RESULT" | python3 -c "
|
|
100
|
+
import sys,json
|
|
101
|
+
d=json.load(sys.stdin)
|
|
102
|
+
paths = d['paths_searched']
|
|
103
|
+
# Leadership should not have unrestricted memory/ or state/ or logs/
|
|
104
|
+
bad = [p for p in paths if p in ('memory/', 'state/', 'logs/')]
|
|
105
|
+
assert len(bad) == 0, f'Should not have: {bad}'
|
|
106
|
+
" 2>/dev/null; then
|
|
107
|
+
pass_test "Leadership search does NOT include unrestricted memory/state/logs"
|
|
108
|
+
else
|
|
109
|
+
fail_test "Leadership should not have unrestricted memory/state/logs access"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# Leadership should have knowledge/
|
|
113
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert 'knowledge/' in d['paths_searched']" 2>/dev/null; then
|
|
114
|
+
pass_test "Leadership search includes knowledge/"
|
|
115
|
+
else
|
|
116
|
+
fail_test "Leadership should include knowledge/"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
echo ""
|
|
120
|
+
|
|
121
|
+
# --- Test 3: Unknown user (minimal access) ---
|
|
122
|
+
echo "Test 3: Unknown user search (user='nobody', query='northwind')"
|
|
123
|
+
RESULT=$(run_search --user nobody --query "northwind" --format json 2>&1 || true)
|
|
124
|
+
|
|
125
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['access_level']=='default'" 2>/dev/null; then
|
|
126
|
+
pass_test "Unknown user gets 'default' access"
|
|
127
|
+
else
|
|
128
|
+
fail_test "Unknown user should get 'default' access"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
PATHS_SEARCHED=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(' '.join(d['paths_searched']))" 2>/dev/null || echo "")
|
|
132
|
+
# Default should ONLY search knowledge/sources/
|
|
133
|
+
if echo "$PATHS_SEARCHED" | grep -q "knowledge/sources/"; then
|
|
134
|
+
pass_test "Default access includes knowledge/sources/"
|
|
135
|
+
else
|
|
136
|
+
fail_test "Default access should include knowledge/sources/"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# Should NOT have memory/ or outputs/ or docs/
|
|
140
|
+
if echo "$PATHS_SEARCHED" | grep -qv "memory\|outputs\|docs\|state\|logs"; then
|
|
141
|
+
pass_test "Default access does NOT include private paths"
|
|
142
|
+
else
|
|
143
|
+
fail_test "Default access should not include memory/outputs/docs/state/logs"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
echo ""
|
|
147
|
+
|
|
148
|
+
# --- Test 4: Slack ID resolution ---
|
|
149
|
+
echo "Test 4: Slack ID resolution (U099N1JGKRQ -> alex)"
|
|
150
|
+
RESULT=$(run_search --user U099N1JGKRQ --query "test" --format json 2>&1 || true)
|
|
151
|
+
|
|
152
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['user']=='alex'" 2>/dev/null; then
|
|
153
|
+
pass_test "Slack ID U099N1JGKRQ resolved to 'alex'"
|
|
154
|
+
else
|
|
155
|
+
fail_test "Slack ID U099N1JGKRQ should resolve to 'alex'"
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['access_level']=='ceo'" 2>/dev/null; then
|
|
159
|
+
pass_test "Resolved user has 'ceo' access"
|
|
160
|
+
else
|
|
161
|
+
fail_test "Resolved user should have 'ceo' access"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
echo ""
|
|
165
|
+
|
|
166
|
+
# --- Test 5: Email resolution ---
|
|
167
|
+
echo "Test 5: Email resolution (nima@example.com -> nima)"
|
|
168
|
+
RESULT=$(run_search --user "nima@example.com" --query "legal" --format json 2>&1 || true)
|
|
169
|
+
|
|
170
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['user']=='nima'" 2>/dev/null; then
|
|
171
|
+
pass_test "Email nima@example.com resolved to 'nima'"
|
|
172
|
+
else
|
|
173
|
+
fail_test "Email nima@example.com should resolve to 'nima'"
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['access_level']=='leadership'" 2>/dev/null; then
|
|
177
|
+
pass_test "Resolved user has 'leadership' access"
|
|
178
|
+
else
|
|
179
|
+
fail_test "Resolved user should have 'leadership' access"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
echo ""
|
|
183
|
+
|
|
184
|
+
# --- Test 6: Text format output ---
|
|
185
|
+
echo "Test 6: Text format output"
|
|
186
|
+
RESULT=$(run_search --user alex --query "the regulator" --format text 2>&1 || true)
|
|
187
|
+
|
|
188
|
+
if echo "$RESULT" | grep -q "Access: ceo"; then
|
|
189
|
+
pass_test "Text format includes access level"
|
|
190
|
+
else
|
|
191
|
+
fail_test "Text format should include 'Access: ceo'"
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
if echo "$RESULT" | grep -q 'Search: "the regulator"'; then
|
|
195
|
+
pass_test "Text format includes query"
|
|
196
|
+
else
|
|
197
|
+
fail_test "Text format should include query"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
echo ""
|
|
201
|
+
|
|
202
|
+
# --- Test 7: Partner access (shayan) ---
|
|
203
|
+
echo "Test 7: Partner access (shayan)"
|
|
204
|
+
RESULT=$(run_search --user shayan --query "northwind" --format json 2>&1 || true)
|
|
205
|
+
|
|
206
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['access_level']=='partner'" 2>/dev/null; then
|
|
207
|
+
pass_test "Casey gets 'partner' access"
|
|
208
|
+
else
|
|
209
|
+
fail_test "Casey should get 'partner' access"
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
# Partner should NOT have full knowledge/ or state/
|
|
213
|
+
if echo "$RESULT" | python3 -c "
|
|
214
|
+
import sys,json
|
|
215
|
+
d=json.load(sys.stdin)
|
|
216
|
+
paths = d['paths_searched']
|
|
217
|
+
bad = [p for p in paths if p in ('knowledge/', 'state/', 'memory/', 'logs/')]
|
|
218
|
+
assert len(bad) == 0, f'Should not have: {bad}'
|
|
219
|
+
" 2>/dev/null; then
|
|
220
|
+
pass_test "Partner does NOT have unrestricted knowledge/state/memory/logs"
|
|
221
|
+
else
|
|
222
|
+
fail_test "Partner should not have unrestricted access"
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
echo ""
|
|
226
|
+
|
|
227
|
+
# --- Test 8: Phone number resolution ---
|
|
228
|
+
echo "Test 8: Phone number resolution (+971585291799 -> alex)"
|
|
229
|
+
RESULT=$(run_search --user "+971585291799" --query "test" --format json 2>&1 || true)
|
|
230
|
+
|
|
231
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['user']=='alex'" 2>/dev/null; then
|
|
232
|
+
pass_test "Phone +971585291799 resolved to 'alex'"
|
|
233
|
+
else
|
|
234
|
+
fail_test "Phone +971585291799 should resolve to 'alex'"
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
echo ""
|
|
238
|
+
|
|
239
|
+
# ── Summary ────────────────────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
echo "================================================================"
|
|
242
|
+
echo "Results: $PASS passed, $FAIL failed, $TOTAL total"
|
|
243
|
+
echo "================================================================"
|
|
244
|
+
|
|
245
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
246
|
+
exit 1
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
echo ""
|
|
250
|
+
echo "All tests passed."
|
|
251
|
+
exit 0
|