ohwow 0.6.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/index.js +3112 -1534
  2. package/dist/mcp-server/index.js +12 -12
  3. package/dist/migrations/091-skills-sop-columns.sql +18 -0
  4. package/dist/migrations/092-operational-pillars.sql +55 -0
  5. package/dist/migrations/093-seed-operational-pillars.sql +176 -0
  6. package/dist/migrations/094-person-models.sql +58 -0
  7. package/dist/migrations/095-transition-engine.sql +52 -0
  8. package/dist/migrations/096-work-router.sql +95 -0
  9. package/dist/migrations/097-human-growth.sql +58 -0
  10. package/dist/migrations/098-observation-layer.sql +5 -0
  11. package/dist/migrations/099-collective-intelligence.sql +5 -0
  12. package/dist/migrations/100-agent-model-policy.sql +32 -0
  13. package/dist/migrations/101-llm-calls.sql +36 -0
  14. package/dist/migrations/102-team-member-guide.sql +14 -0
  15. package/dist/migrations/103-fix-person-models-fk.sql +89 -0
  16. package/dist/migrations/104-fix-person-observations-fk.sql +48 -0
  17. package/dist/migrations/105-onboarding-plans.sql +56 -0
  18. package/dist/migrations/106-deliverable-actor-link.sql +29 -0
  19. package/dist/migrations/107-code-skills.sql +20 -0
  20. package/dist/migrations/108-archive-procedure-skills.sql +29 -0
  21. package/dist/migrations/109-workspace-default-fs-paths.sql +19 -0
  22. package/dist/migrations/110-task-state-ttl.sql +28 -0
  23. package/dist/migrations/111-conversation-status.sql +25 -0
  24. package/dist/migrations/112-deliverables-created-at-iso.sql +40 -0
  25. package/dist/migrations/113-permission-requests.sql +36 -0
  26. package/dist/migrations/114-llm-calls-tool-telemetry.sql +32 -0
  27. package/dist/migrations/115-trigger-watchdog.sql +46 -0
  28. package/dist/migrations/116-self-findings.sql +46 -0
  29. package/dist/migrations/117-experiment-validations.sql +64 -0
  30. package/dist/migrations/118-validation-rollback.sql +33 -0
  31. package/dist/migrations/119-runtime-config-overrides.sql +44 -0
  32. package/dist/migrations/120-business-vitals.sql +44 -0
  33. package/dist/migrations/121-x-contact-events.sql +42 -0
  34. package/dist/migrations/122-video-jobs.sql +52 -0
  35. package/dist/migrations/123-insight-distiller.sql +68 -0
  36. package/dist/migrations/124-x-dm-messages.sql +55 -0
  37. package/dist/migrations/125-x-dm-messages-bodies.sql +40 -0
  38. package/dist/migrations/126-x-dm-signals.sql +52 -0
  39. package/dist/migrations/127-x-dm-contact-linking.sql +36 -0
  40. package/dist/migrations/128-attribution-view.sql +59 -0
  41. package/dist/migrations/129-x-posted-log.sql +36 -0
  42. package/dist/migrations/130-patches-attempted-log.sql +44 -0
  43. package/dist/scrapling-server/requirements.txt +3 -0
  44. package/dist/scrapling-server/server.py +224 -0
  45. package/dist/web/assets/index-Bp9CoQ8c.css +1 -0
  46. package/dist/web/assets/index-C5xtuLcg.js +102 -0
  47. package/dist/web/index.html +2 -2
  48. package/package.json +9 -4
  49. package/dist/web/assets/index-B2PzvKIq.js +0 -100
  50. package/dist/web/assets/index-DZAi92e-.css +0 -1
@@ -0,0 +1,52 @@
1
+ -- Transition Engine: task patterns + task transitions (SQLite)
2
+
3
+ CREATE TABLE IF NOT EXISTS task_patterns (
4
+ id TEXT PRIMARY KEY,
5
+ workspace_id TEXT NOT NULL,
6
+ person_model_id TEXT,
7
+ name TEXT NOT NULL,
8
+ description TEXT,
9
+ category TEXT NOT NULL DEFAULT 'general' CHECK (category IN (
10
+ 'email', 'content', 'research', 'data', 'social',
11
+ 'scheduling', 'crm', 'support', 'ops', 'general'
12
+ )),
13
+ detection_method TEXT NOT NULL DEFAULT 'auto_detected' CHECK (detection_method IN ('manual', 'auto_detected', 'pillar_derived')),
14
+ frequency TEXT CHECK (frequency IN ('daily', 'weekly', 'biweekly', 'monthly', 'irregular')),
15
+ avg_human_duration_minutes INTEGER,
16
+ avg_agent_duration_minutes INTEGER,
17
+ title_keywords TEXT DEFAULT '[]',
18
+ tool_fingerprint TEXT DEFAULT '[]',
19
+ department_id TEXT,
20
+ preferred_agent_id TEXT,
21
+ instance_count INTEGER DEFAULT 0,
22
+ first_observed_at TEXT DEFAULT (datetime('now')),
23
+ last_observed_at TEXT DEFAULT (datetime('now')),
24
+ archived_at TEXT,
25
+ created_at TEXT DEFAULT (datetime('now')),
26
+ updated_at TEXT DEFAULT (datetime('now'))
27
+ );
28
+
29
+ CREATE TABLE IF NOT EXISTS task_transitions (
30
+ id TEXT PRIMARY KEY,
31
+ workspace_id TEXT NOT NULL,
32
+ pattern_id TEXT NOT NULL,
33
+ person_model_id TEXT,
34
+ current_stage INTEGER NOT NULL DEFAULT 1 CHECK (current_stage BETWEEN 1 AND 5),
35
+ stage_history TEXT DEFAULT '[]',
36
+ confidence_score REAL DEFAULT 0.0,
37
+ correction_count INTEGER DEFAULT 0,
38
+ total_instances INTEGER DEFAULT 0,
39
+ successful_instances INTEGER DEFAULT 0,
40
+ human_edit_rate REAL DEFAULT 1.0,
41
+ last_promoted_at TEXT,
42
+ last_demoted_at TEXT,
43
+ time_saved_minutes INTEGER DEFAULT 0,
44
+ active INTEGER NOT NULL DEFAULT 1,
45
+ created_at TEXT DEFAULT (datetime('now')),
46
+ updated_at TEXT DEFAULT (datetime('now'))
47
+ );
48
+
49
+ CREATE INDEX IF NOT EXISTS idx_task_patterns_workspace ON task_patterns(workspace_id);
50
+ CREATE INDEX IF NOT EXISTS idx_task_patterns_person ON task_patterns(workspace_id, person_model_id);
51
+ CREATE INDEX IF NOT EXISTS idx_task_transitions_pattern ON task_transitions(pattern_id);
52
+ CREATE INDEX IF NOT EXISTS idx_task_transitions_workspace ON task_transitions(workspace_id);
@@ -0,0 +1,95 @@
1
+ -- Work Router: intelligent task routing + work augmentation + notification prefs
2
+ -- Phase 3 of Center of Operations
3
+
4
+ -- Routing decisions: tracks how tasks are assigned and why
5
+ CREATE TABLE IF NOT EXISTS work_routing_decisions (
6
+ id TEXT PRIMARY KEY,
7
+ workspace_id TEXT NOT NULL,
8
+ task_id TEXT,
9
+ task_title TEXT NOT NULL,
10
+ task_urgency TEXT DEFAULT 'normal' CHECK (task_urgency IN ('low', 'normal', 'high', 'critical')),
11
+ required_skills TEXT DEFAULT '[]',
12
+ estimated_effort_minutes INTEGER,
13
+
14
+ -- Assignment
15
+ assigned_to_type TEXT NOT NULL CHECK (assigned_to_type IN ('person', 'agent')),
16
+ assigned_to_id TEXT NOT NULL,
17
+ assignment_method TEXT NOT NULL DEFAULT 'recommended' CHECK (assignment_method IN ('auto', 'recommended', 'manual', 'fallback')),
18
+ confidence_score REAL DEFAULT 0.0 CHECK (confidence_score >= 0 AND confidence_score <= 1),
19
+
20
+ -- Scoring breakdown (JSON: { skill: 0.9, capacity: 0.7, energy: 0.8, ... })
21
+ score_breakdown TEXT DEFAULT '{}',
22
+ runner_up_id TEXT,
23
+ runner_up_type TEXT CHECK (runner_up_type IN ('person', 'agent')),
24
+ runner_up_score REAL,
25
+
26
+ -- Outcome tracking
27
+ outcome TEXT CHECK (outcome IN ('completed', 'reassigned', 'rejected', 'timed_out')),
28
+ outcome_quality_score REAL CHECK (outcome_quality_score >= 0 AND outcome_quality_score <= 1),
29
+ actual_effort_minutes INTEGER,
30
+ completed_at TEXT,
31
+
32
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
33
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ );
35
+
36
+ -- Work augmentation: pre/co/post work for human-assigned tasks
37
+ CREATE TABLE IF NOT EXISTS work_augmentations (
38
+ id TEXT PRIMARY KEY,
39
+ workspace_id TEXT NOT NULL,
40
+ routing_decision_id TEXT REFERENCES work_routing_decisions(id) ON DELETE CASCADE,
41
+ task_id TEXT,
42
+ person_model_id TEXT,
43
+
44
+ -- Phase
45
+ phase TEXT NOT NULL CHECK (phase IN ('pre', 'co', 'post')),
46
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'skipped', 'failed')),
47
+
48
+ -- What the augmentation does
49
+ augmentation_type TEXT NOT NULL,
50
+ description TEXT,
51
+ agent_id TEXT,
52
+
53
+ -- Output
54
+ output TEXT DEFAULT '{}',
55
+ was_useful INTEGER,
56
+
57
+ started_at TEXT,
58
+ completed_at TEXT,
59
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
60
+ );
61
+
62
+ -- Notification preferences per person (extends person model)
63
+ CREATE TABLE IF NOT EXISTS notification_preferences (
64
+ id TEXT PRIMARY KEY,
65
+ workspace_id TEXT NOT NULL,
66
+ person_model_id TEXT NOT NULL,
67
+
68
+ -- Channel preferences
69
+ preferred_channel TEXT DEFAULT 'in_app' CHECK (preferred_channel IN ('in_app', 'email', 'slack', 'telegram', 'whatsapp')),
70
+ fallback_channel TEXT DEFAULT 'email' CHECK (fallback_channel IN ('in_app', 'email', 'slack', 'telegram', 'whatsapp')),
71
+
72
+ -- Deep work protection
73
+ deep_work_start TEXT,
74
+ deep_work_end TEXT,
75
+ deep_work_days TEXT DEFAULT '[]',
76
+ buffer_during_deep_work INTEGER NOT NULL DEFAULT 1,
77
+
78
+ -- Energy-aware filtering
79
+ low_energy_start TEXT,
80
+ low_energy_end TEXT,
81
+ suppress_complex_during_low_energy INTEGER NOT NULL DEFAULT 1,
82
+
83
+ -- Urgency thresholds
84
+ min_urgency_for_interrupt TEXT DEFAULT 'high' CHECK (min_urgency_for_interrupt IN ('low', 'normal', 'high', 'critical')),
85
+
86
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
87
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
88
+ );
89
+
90
+ CREATE INDEX IF NOT EXISTS idx_routing_decisions_workspace ON work_routing_decisions(workspace_id);
91
+ CREATE INDEX IF NOT EXISTS idx_routing_decisions_assignee ON work_routing_decisions(assigned_to_type, assigned_to_id);
92
+ CREATE INDEX IF NOT EXISTS idx_routing_decisions_task ON work_routing_decisions(task_id);
93
+ CREATE INDEX IF NOT EXISTS idx_augmentations_routing ON work_augmentations(routing_decision_id);
94
+ CREATE INDEX IF NOT EXISTS idx_augmentations_workspace ON work_augmentations(workspace_id);
95
+ CREATE INDEX IF NOT EXISTS idx_notification_prefs_person ON notification_preferences(person_model_id);
@@ -0,0 +1,58 @@
1
+ -- Human Growth Engine: skill progression, growth milestones, delegation tracking
2
+ -- Phase 4 of Center of Operations
3
+
4
+ -- Skill progression: tracks skill level changes over time per person
5
+ CREATE TABLE IF NOT EXISTS skill_progression (
6
+ id TEXT PRIMARY KEY,
7
+ workspace_id TEXT NOT NULL,
8
+ person_model_id TEXT NOT NULL REFERENCES agent_workforce_person_models(id) ON DELETE CASCADE,
9
+ skill_name TEXT NOT NULL,
10
+ previous_level REAL DEFAULT 0.0,
11
+ new_level REAL NOT NULL,
12
+ source TEXT NOT NULL DEFAULT 'task_outcome' CHECK (source IN (
13
+ 'task_outcome', 'self_assessment', 'peer_observation', 'training', 'routing_feedback'
14
+ )),
15
+ task_id TEXT,
16
+ notes TEXT,
17
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
18
+ );
19
+
20
+ -- Growth milestones: target achievements on skill development paths
21
+ CREATE TABLE IF NOT EXISTS growth_milestones (
22
+ id TEXT PRIMARY KEY,
23
+ workspace_id TEXT NOT NULL,
24
+ person_model_id TEXT NOT NULL REFERENCES agent_workforce_person_models(id) ON DELETE CASCADE,
25
+ skill_name TEXT NOT NULL,
26
+ target_level REAL NOT NULL DEFAULT 0.7,
27
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'achieved', 'abandoned')),
28
+ difficulty TEXT NOT NULL DEFAULT 'intermediate' CHECK (difficulty IN ('beginner', 'intermediate', 'advanced', 'expert')),
29
+ suggested_tasks TEXT DEFAULT '[]',
30
+ scaffolding_level TEXT NOT NULL DEFAULT 'high' CHECK (scaffolding_level IN ('high', 'medium', 'low', 'none')),
31
+ path_order INTEGER DEFAULT 0,
32
+ achieved_at TEXT,
33
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
34
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
35
+ );
36
+
37
+ -- Delegation decisions: founder-specific tracking of what gets delegated
38
+ CREATE TABLE IF NOT EXISTS delegation_decisions (
39
+ id TEXT PRIMARY KEY,
40
+ workspace_id TEXT NOT NULL,
41
+ person_model_id TEXT NOT NULL REFERENCES agent_workforce_person_models(id) ON DELETE CASCADE,
42
+ decision_type TEXT NOT NULL,
43
+ description TEXT,
44
+ delegated_to_type TEXT CHECK (delegated_to_type IN ('agent', 'person')),
45
+ delegated_to_id TEXT,
46
+ outcome TEXT DEFAULT 'pending' CHECK (outcome IN ('successful', 'reverted', 'pending')),
47
+ founder_review_needed INTEGER NOT NULL DEFAULT 1,
48
+ routing_decision_id TEXT,
49
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
50
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
51
+ );
52
+
53
+ CREATE INDEX IF NOT EXISTS idx_skill_progression_person ON skill_progression(person_model_id);
54
+ CREATE INDEX IF NOT EXISTS idx_skill_progression_skill ON skill_progression(person_model_id, skill_name);
55
+ CREATE INDEX IF NOT EXISTS idx_growth_milestones_person ON growth_milestones(person_model_id);
56
+ CREATE INDEX IF NOT EXISTS idx_growth_milestones_status ON growth_milestones(person_model_id, status);
57
+ CREATE INDEX IF NOT EXISTS idx_delegation_decisions_person ON delegation_decisions(person_model_id);
58
+ CREATE INDEX IF NOT EXISTS idx_delegation_decisions_workspace ON delegation_decisions(workspace_id);
@@ -0,0 +1,5 @@
1
+ -- Observation Layer: add work_pattern_map to person models
2
+ -- Phase 5 of Center of Operations (integration-only approach)
3
+
4
+ ALTER TABLE agent_workforce_person_models
5
+ ADD COLUMN work_pattern_map TEXT DEFAULT '{}';
@@ -0,0 +1,5 @@
1
+ -- Collective Intelligence: add collective_briefing to person models
2
+ -- Phase 6 of Center of Operations (capstone)
3
+
4
+ ALTER TABLE agent_workforce_person_models
5
+ ADD COLUMN collective_briefing TEXT DEFAULT '{}';
@@ -0,0 +1,32 @@
1
+ -- Shape C: agents are sub-orchestrators, not single-model wrappers.
2
+ --
3
+ -- Moves any existing `config.model` string on agent rows into
4
+ -- `config.model_policy.default`, then removes the legacy `config.model` key
5
+ -- so downstream code never reads it again. Agents that already carry a
6
+ -- `model_policy` are left alone. Agents without a model at all are also
7
+ -- untouched.
8
+ --
9
+ -- Idempotent and safe to re-run: the WHERE clause narrows to rows that
10
+ -- both (a) still have `config.model` and (b) do not yet have
11
+ -- `config.model_policy.default`.
12
+
13
+ UPDATE agent_workforce_agents
14
+ SET config = json_remove(
15
+ json_set(
16
+ config,
17
+ '$.model_policy.default',
18
+ json_extract(config, '$.model')
19
+ ),
20
+ '$.model'
21
+ )
22
+ WHERE json_extract(config, '$.model') IS NOT NULL
23
+ AND json_extract(config, '$.model_policy.default') IS NULL;
24
+
25
+ -- For rows that had `config.model` AND already had a `model_policy` but
26
+ -- no default set: the previous UPDATE handled the "no default" case. For
27
+ -- rows with both a legacy `config.model` AND an existing
28
+ -- `model_policy.default`, we drop the legacy key without overwriting the
29
+ -- policy — the policy wins.
30
+ UPDATE agent_workforce_agents
31
+ SET config = json_remove(config, '$.model')
32
+ WHERE json_extract(config, '$.model') IS NOT NULL;
@@ -0,0 +1,36 @@
1
+ -- Shape C telemetry: per-call record of every llm organ invocation.
2
+ --
3
+ -- Every runLlmCall writes a row here, success or failure, with the
4
+ -- resolved provider, model, token counts, cost, and latency. Feeds
5
+ -- future adaptive routing (routingHistory, bandit model selection) and
6
+ -- lets operators see cost per agent per purpose over time.
7
+ --
8
+ -- Nullable agent_id: llm calls can come from orchestrator chat (no
9
+ -- specific agent) or from external callers via /api/llm.
10
+ -- Nullable task_id: llm calls may be ad-hoc outside any task.
11
+
12
+ CREATE TABLE IF NOT EXISTS llm_calls (
13
+ id TEXT PRIMARY KEY,
14
+ workspace_id TEXT NOT NULL,
15
+ agent_id TEXT,
16
+ task_id TEXT,
17
+ purpose TEXT NOT NULL,
18
+ provider TEXT NOT NULL,
19
+ model TEXT NOT NULL,
20
+ input_tokens INTEGER NOT NULL DEFAULT 0,
21
+ output_tokens INTEGER NOT NULL DEFAULT 0,
22
+ cost_cents INTEGER NOT NULL DEFAULT 0,
23
+ latency_ms INTEGER NOT NULL DEFAULT 0,
24
+ success INTEGER NOT NULL DEFAULT 1,
25
+ error_message TEXT,
26
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
27
+ );
28
+
29
+ CREATE INDEX IF NOT EXISTS llm_calls_workspace_created_idx
30
+ ON llm_calls(workspace_id, created_at DESC);
31
+
32
+ CREATE INDEX IF NOT EXISTS llm_calls_agent_idx
33
+ ON llm_calls(agent_id, created_at DESC);
34
+
35
+ CREATE INDEX IF NOT EXISTS llm_calls_purpose_idx
36
+ ON llm_calls(purpose, created_at DESC);
@@ -0,0 +1,14 @@
1
+ -- 102-team-member-guide.sql
2
+ -- Link a team member to their dedicated "chief of staff" guide agent.
3
+ -- Also adds timezone, cloud_invite_token (for pending invites), and
4
+ -- onboarding_status so the orchestrator can track whether person
5
+ -- ingestion has been kicked off / completed.
6
+
7
+ ALTER TABLE agent_workforce_team_members ADD COLUMN assigned_guide_agent_id TEXT DEFAULT NULL;
8
+ ALTER TABLE agent_workforce_team_members ADD COLUMN timezone TEXT DEFAULT NULL;
9
+ ALTER TABLE agent_workforce_team_members ADD COLUMN cloud_invite_token TEXT DEFAULT NULL;
10
+ ALTER TABLE agent_workforce_team_members ADD COLUMN cloud_invite_status TEXT DEFAULT NULL;
11
+ ALTER TABLE agent_workforce_team_members ADD COLUMN onboarding_status TEXT DEFAULT 'not_started';
12
+
13
+ CREATE INDEX IF NOT EXISTS idx_team_members_guide
14
+ ON agent_workforce_team_members(assigned_guide_agent_id);
@@ -0,0 +1,89 @@
1
+ -- 103-fix-person-models-fk.sql
2
+ -- Migration 094 declared team_member_id as REFERENCES team_members(id) but
3
+ -- the real table is agent_workforce_team_members. With foreign_keys = ON
4
+ -- (set in db/init.ts) any INSERT/UPDATE on agent_workforce_person_models
5
+ -- fails with "no such table: main.team_members" because SQLite resolves
6
+ -- the FK target lazily, and the target does not exist.
7
+ --
8
+ -- SQLite has no ALTER TABLE DROP CONSTRAINT, so we rebuild the table:
9
+ -- rename the old one, CREATE the new shape with the correct FK, copy rows,
10
+ -- drop the rename. Wrapped in a transaction so either everything lands or
11
+ -- nothing does.
12
+ --
13
+ -- The new FK points at agent_workforce_team_members(id) with ON DELETE
14
+ -- SET NULL matching the original intent.
15
+
16
+ -- The migration runner already wraps each file in a transaction, so we
17
+ -- do not BEGIN/COMMIT ourselves. foreign_keys is set at init time; the
18
+ -- rebuild-via-rename pattern works here because we copy rows between
19
+ -- two tables in the same workspace and the only broken FK is the one
20
+ -- we are fixing.
21
+
22
+ ALTER TABLE agent_workforce_person_models RENAME TO agent_workforce_person_models_old;
23
+
24
+ CREATE TABLE agent_workforce_person_models (
25
+ id TEXT PRIMARY KEY,
26
+ workspace_id TEXT NOT NULL REFERENCES agent_workforce_workspaces(id) ON DELETE CASCADE,
27
+ team_member_id TEXT REFERENCES agent_workforce_team_members(id) ON DELETE SET NULL,
28
+ name TEXT NOT NULL,
29
+ email TEXT,
30
+ avatar_url TEXT,
31
+ role_title TEXT,
32
+ variant TEXT NOT NULL DEFAULT 'team_member' CHECK (variant IN ('founder', 'team_member', 'new_hire')),
33
+ work_history TEXT DEFAULT '[]',
34
+ skills_map TEXT DEFAULT '{}',
35
+ domain_expertise TEXT DEFAULT '{}',
36
+ blind_spots TEXT DEFAULT '[]',
37
+ tool_proficiency TEXT DEFAULT '{}',
38
+ communication_style TEXT DEFAULT '{}',
39
+ energy_patterns TEXT DEFAULT '{}',
40
+ learning_style TEXT,
41
+ collaboration_preferences TEXT DEFAULT '{}',
42
+ ambitions TEXT DEFAULT '{}',
43
+ values_and_motivations TEXT DEFAULT '[]',
44
+ friction_points TEXT DEFAULT '[]',
45
+ flow_triggers TEXT DEFAULT '[]',
46
+ skill_gaps_to_close TEXT DEFAULT '[]',
47
+ external_context TEXT DEFAULT '{}',
48
+ growth_arc TEXT DEFAULT '{}',
49
+ growth_velocity REAL DEFAULT 0,
50
+ growth_direction TEXT DEFAULT 'ascending' CHECK (growth_direction IN ('ascending', 'plateau', 'declining', 'transforming')),
51
+ growth_snapshots TEXT DEFAULT '[]',
52
+ ingestion_status TEXT DEFAULT 'not_started' CHECK (ingestion_status IN ('not_started', 'in_progress', 'initial_complete', 'mature')),
53
+ ingestion_variant TEXT,
54
+ last_ingestion_at TEXT,
55
+ observation_count INTEGER DEFAULT 0,
56
+ refinement_count INTEGER DEFAULT 0,
57
+ work_pattern_map TEXT DEFAULT '{}',
58
+ collective_briefing TEXT DEFAULT '{}',
59
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
60
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
61
+ );
62
+
63
+ INSERT INTO agent_workforce_person_models (
64
+ id, workspace_id, team_member_id, name, email, avatar_url, role_title,
65
+ variant, work_history, skills_map, domain_expertise, blind_spots,
66
+ tool_proficiency, communication_style, energy_patterns, learning_style,
67
+ collaboration_preferences, ambitions, values_and_motivations,
68
+ friction_points, flow_triggers, skill_gaps_to_close, external_context,
69
+ growth_arc, growth_velocity, growth_direction, growth_snapshots,
70
+ ingestion_status, ingestion_variant, last_ingestion_at,
71
+ observation_count, refinement_count, work_pattern_map, collective_briefing,
72
+ created_at, updated_at
73
+ )
74
+ SELECT
75
+ id, workspace_id, team_member_id, name, email, avatar_url, role_title,
76
+ variant, work_history, skills_map, domain_expertise, blind_spots,
77
+ tool_proficiency, communication_style, energy_patterns, learning_style,
78
+ collaboration_preferences, ambitions, values_and_motivations,
79
+ friction_points, flow_triggers, skill_gaps_to_close, external_context,
80
+ growth_arc, growth_velocity, growth_direction, growth_snapshots,
81
+ ingestion_status, ingestion_variant, last_ingestion_at,
82
+ observation_count, refinement_count, work_pattern_map, collective_briefing,
83
+ created_at, updated_at
84
+ FROM agent_workforce_person_models_old;
85
+
86
+ DROP TABLE agent_workforce_person_models_old;
87
+
88
+ CREATE INDEX IF NOT EXISTS idx_person_models_workspace
89
+ ON agent_workforce_person_models(workspace_id);
@@ -0,0 +1,48 @@
1
+ -- 104-fix-person-observations-fk.sql
2
+ --
3
+ -- Migration 103 rebuilt agent_workforce_person_models with the correct FK
4
+ -- target (agent_workforce_team_members), via the RENAME + CREATE + INSERT +
5
+ -- DROP dance. But the child table agent_workforce_person_observations
6
+ -- declares its FK as:
7
+ -- person_model_id TEXT NOT NULL REFERENCES "agent_workforce_person_models_old"(id) ON DELETE CASCADE
8
+ -- SQLite baked the rename target into the observations FK at the moment of
9
+ -- RENAME, and now observations point at a dropped table. Any insert fails
10
+ -- with "no such table: main.agent_workforce_person_models_old" because
11
+ -- foreign_keys is on in db/init.ts.
12
+ --
13
+ -- Rebuild the observations table too so the FK points back at
14
+ -- agent_workforce_person_models(id). Same copy-via-rename pattern as 103.
15
+ -- The CHECK and indexes are recreated to match.
16
+
17
+ ALTER TABLE agent_workforce_person_observations RENAME TO agent_workforce_person_observations_old;
18
+
19
+ CREATE TABLE agent_workforce_person_observations (
20
+ id TEXT PRIMARY KEY,
21
+ person_model_id TEXT NOT NULL REFERENCES agent_workforce_person_models(id) ON DELETE CASCADE,
22
+ workspace_id TEXT NOT NULL REFERENCES agent_workforce_workspaces(id) ON DELETE CASCADE,
23
+ dimension TEXT NOT NULL,
24
+ observation_type TEXT NOT NULL CHECK (observation_type IN (
25
+ 'task_outcome', 'communication', 'feedback', 'self_report', 'behavioral', 'peer_observation', 'correction'
26
+ )),
27
+ content TEXT NOT NULL,
28
+ data TEXT DEFAULT '{}',
29
+ confidence REAL DEFAULT 0.5 CHECK (confidence >= 0 AND confidence <= 1),
30
+ processed INTEGER DEFAULT 0,
31
+ source_type TEXT,
32
+ source_id TEXT,
33
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ );
35
+
36
+ INSERT INTO agent_workforce_person_observations (
37
+ id, person_model_id, workspace_id, dimension, observation_type, content,
38
+ data, confidence, processed, source_type, source_id, created_at
39
+ )
40
+ SELECT
41
+ id, person_model_id, workspace_id, dimension, observation_type, content,
42
+ data, confidence, processed, source_type, source_id, created_at
43
+ FROM agent_workforce_person_observations_old;
44
+
45
+ DROP TABLE agent_workforce_person_observations_old;
46
+
47
+ CREATE INDEX IF NOT EXISTS idx_person_observations_model
48
+ ON agent_workforce_person_observations(person_model_id);
@@ -0,0 +1,56 @@
1
+ -- 105-onboarding-plans.sql
2
+ --
3
+ -- First-class onboarding plan artifact per team member. The COS agent
4
+ -- generates a 4-week ramp plan during person-model ingestion via
5
+ -- propose_first_month_plan, and that draft lives here until the member
6
+ -- (via the chat) accepts it — at which point accept_onboarding_plan
7
+ -- materializes it into real agent_workforce_tasks + agent_workforce_goals
8
+ -- rows and flips status to 'accepted'.
9
+ --
10
+ -- Shape:
11
+ --
12
+ -- weeks: JSON array, one entry per week:
13
+ -- [
14
+ -- {
15
+ -- "week": 1,
16
+ -- "theme": "Land + observe",
17
+ -- "tasks": [
18
+ -- { "title": ..., "description": ..., "owner": "member|guide|<agent-id>",
19
+ -- "success_criteria": ..., "materialized_task_id": null }
20
+ -- ]
21
+ -- },
22
+ -- ...
23
+ -- ]
24
+ --
25
+ -- status:
26
+ -- 'draft' — freshly proposed, member hasn't reviewed yet
27
+ -- 'accepted' — member accepted, tasks + goals created
28
+ -- 'in_progress' — at least one materialized task has been started
29
+ -- 'completed' — all materialized tasks are done
30
+ -- 'archived' — superseded or canceled
31
+
32
+ CREATE TABLE IF NOT EXISTS agent_workforce_onboarding_plans (
33
+ id TEXT PRIMARY KEY,
34
+ workspace_id TEXT NOT NULL REFERENCES agent_workforce_workspaces(id) ON DELETE CASCADE,
35
+ team_member_id TEXT NOT NULL REFERENCES agent_workforce_team_members(id) ON DELETE CASCADE,
36
+ person_model_id TEXT REFERENCES agent_workforce_person_models(id) ON DELETE SET NULL,
37
+ created_by_agent_id TEXT REFERENCES agent_workforce_agents(id) ON DELETE SET NULL,
38
+ status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'accepted', 'in_progress', 'completed', 'archived')),
39
+ rationale TEXT,
40
+ closing_question TEXT,
41
+ weeks TEXT NOT NULL DEFAULT '[]',
42
+ model_used TEXT,
43
+ provider TEXT,
44
+ input_tokens INTEGER DEFAULT 0,
45
+ output_tokens INTEGER DEFAULT 0,
46
+ accepted_at TEXT,
47
+ completed_at TEXT,
48
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
49
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
50
+ );
51
+
52
+ CREATE INDEX IF NOT EXISTS idx_onboarding_plans_member
53
+ ON agent_workforce_onboarding_plans(team_member_id, created_at DESC);
54
+
55
+ CREATE INDEX IF NOT EXISTS idx_onboarding_plans_workspace_status
56
+ ON agent_workforce_onboarding_plans(workspace_id, status);
@@ -0,0 +1,29 @@
1
+ -- 106-deliverable-actor-link.sql
2
+ --
3
+ -- Add three columns to agent_workforce_deliverables so we can attribute
4
+ -- every produced artifact to who made it AND who it was made for.
5
+ --
6
+ -- produced_by_type 'agent' | 'member' | 'guide' | 'system'
7
+ -- produced_by_id agent_id, team_member_id, or null
8
+ -- for_team_member_id the team_member this deliverable belongs to,
9
+ -- e.g. when the COS runs a research task on
10
+ -- Mario's behalf the resulting cheat sheet is
11
+ -- produced_by_type='guide', produced_by_id=COS,
12
+ -- for_team_member_id=Mario.
13
+ --
14
+ -- The cloud activity timeline + per-person work tracking dashboards
15
+ -- query these columns to render "what did agent X produce" and
16
+ -- "what did member Y produce / receive on their behalf".
17
+
18
+ ALTER TABLE agent_workforce_deliverables ADD COLUMN produced_by_type TEXT;
19
+ -- @statement
20
+ ALTER TABLE agent_workforce_deliverables ADD COLUMN produced_by_id TEXT;
21
+ -- @statement
22
+ ALTER TABLE agent_workforce_deliverables ADD COLUMN for_team_member_id TEXT;
23
+ -- @statement
24
+ CREATE INDEX IF NOT EXISTS idx_deliverables_for_team_member
25
+ ON agent_workforce_deliverables(for_team_member_id)
26
+ WHERE for_team_member_id IS NOT NULL;
27
+ -- @statement
28
+ CREATE INDEX IF NOT EXISTS idx_deliverables_produced_by
29
+ ON agent_workforce_deliverables(produced_by_type, produced_by_id);
@@ -0,0 +1,20 @@
1
+ -- Code skills: add columns that let the synthesis pipeline store
2
+ -- deterministic TypeScript-backed skills alongside the existing
3
+ -- procedure/judgement/extraction/verification skill_types.
4
+ --
5
+ -- Local SQLite has no CHECK constraint on skill_type, so adding a new
6
+ -- value ('code') is a no-op at the schema level. The new columns are
7
+ -- all nullable or default-initialized so existing rows remain valid.
8
+ --
9
+ -- Mirror migration lives at ohwow.fun/sql/359-code-skills.sql.
10
+
11
+ ALTER TABLE agent_workforce_skills ADD COLUMN script_path TEXT;
12
+ ALTER TABLE agent_workforce_skills ADD COLUMN selectors TEXT DEFAULT '{}';
13
+ ALTER TABLE agent_workforce_skills ADD COLUMN origin_trace_id TEXT;
14
+ ALTER TABLE agent_workforce_skills ADD COLUMN success_count INTEGER NOT NULL DEFAULT 0;
15
+ ALTER TABLE agent_workforce_skills ADD COLUMN fail_count INTEGER NOT NULL DEFAULT 0;
16
+ ALTER TABLE agent_workforce_skills ADD COLUMN promoted_at TEXT;
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_skills_script_path
19
+ ON agent_workforce_skills(script_path)
20
+ WHERE script_path IS NOT NULL;
@@ -0,0 +1,29 @@
1
+ -- Archive all active procedure skills.
2
+ --
3
+ -- Context: the runtime's three keyword matchers (runAgent SOP loop,
4
+ -- engine compileSkills, prompt-builder triggerMatched) were removed
5
+ -- in the unified-skill-synthesis refactor. Procedure rows no longer
6
+ -- have any discovery path at runtime. Setting is_active=0 archives
7
+ -- them without losing the audit trail. Rows remain queryable for
8
+ -- historical analysis but stop appearing in the skill loader's
9
+ -- boot scan and the now-deleted matchers.
10
+ --
11
+ -- The three active rows on launch eve were all degenerate single-
12
+ -- tool wrappers:
13
+ -- - "Post a Tweet" → tool_sequence ["x_compose_tweet"]
14
+ -- - "Write X Article" → tool_sequence ["x_compose_article"]
15
+ -- - "Check X Messages" → tool_sequence ["x_list_dms"]
16
+ -- The LLM already has x_compose_tweet, x_compose_article, and
17
+ -- x_list_dms directly in its static tool list. No capability is lost.
18
+ --
19
+ -- The single inactive desktop SOP ("Check X Notifications") is
20
+ -- already is_active=0 and is unaffected.
21
+ --
22
+ -- Idempotent: safe to re-run. The `updated_at` stamp moves on each
23
+ -- run but no functional state changes after the first application.
24
+
25
+ UPDATE agent_workforce_skills
26
+ SET is_active = 0,
27
+ updated_at = datetime('now')
28
+ WHERE skill_type = 'procedure'
29
+ AND is_active = 1;
@@ -0,0 +1,19 @@
1
+ -- Workspace-level default filesystem paths.
2
+ --
3
+ -- Without this, every agent that doesn't have an explicit row in
4
+ -- agent_file_access_paths fails every local_read_file/local_write_file call
5
+ -- with "No directories are configured for file access". The orchestrator
6
+ -- pseudo-agent has a hardcoded /tmp baseline (filesystem.ts) but real agents
7
+ -- inherit nothing, so SOP-delegated work that touches the disk silently fails.
8
+ --
9
+ -- engine.ts unions this column with per-agent paths when constructing the
10
+ -- FileAccessGuard, so workspace defaults flow to every task execution.
11
+ -- filesystem.ts reads the same column so orchestrator chat and agent task
12
+ -- execution share a single source of truth for "what /tmp-like paths are
13
+ -- always allowed."
14
+ --
15
+ -- Default value gives every existing workspace /tmp out of the box, matching
16
+ -- the prior orchestrator-only baseline.
17
+
18
+ ALTER TABLE agent_workforce_workspaces
19
+ ADD COLUMN default_filesystem_paths TEXT NOT NULL DEFAULT '["/tmp"]';
@@ -0,0 +1,28 @@
1
+ -- TTL + lazy cleanup for agent_workforce_task_state.
2
+ --
3
+ -- The state table had no expiration mechanism, so once an agent wrote a key
4
+ -- (incident flag, health check, scratch value, etc.) it lived forever. Stale
5
+ -- rows from old sessions polluted reasoning: agents would get_state on a key
6
+ -- that was set during a long-dead incident, see RED, and refuse the unrelated
7
+ -- task in front of them.
8
+ --
9
+ -- expires_at is NULL for persistent state (the default for keys that don't
10
+ -- match the heuristic prefixes in state.ts). For ephemeral keys (incident_*,
11
+ -- *_health_*, temp_*, scratch_*) state.ts now writes a 24h default expiry.
12
+ -- getAgentState filters expired rows on read and lazy-deletes them so the
13
+ -- next reader doesn't trip the same poisoning.
14
+ --
15
+ -- The DELETE at the bottom is a one-shot purge of the two known-poisoning
16
+ -- keys observed in production today. They are not written by any current
17
+ -- code path (verified) so deleting them is a clean fix; they cannot grow
18
+ -- back through normal use.
19
+
20
+ ALTER TABLE agent_workforce_task_state
21
+ ADD COLUMN expires_at TEXT;
22
+
23
+ CREATE INDEX IF NOT EXISTS idx_task_state_expires
24
+ ON agent_workforce_task_state(expires_at)
25
+ WHERE expires_at IS NOT NULL;
26
+
27
+ DELETE FROM agent_workforce_task_state
28
+ WHERE key IN ('last_health_check', 'incident_scrapling_down');
@@ -0,0 +1,25 @@
1
+ -- Async chat: status tracking on orchestrator_conversations.
2
+ --
3
+ -- The MCP ohwow_chat tool now creates a conversation row, returns its id
4
+ -- immediately, and dispatches the orchestrator turn in the background so
5
+ -- long turns survive client disconnects. Clients poll GET /api/chat/:id
6
+ -- until status flips out of 'running'.
7
+ --
8
+ -- Status values:
9
+ -- idle — conversation exists, no turn currently executing (manual create)
10
+ -- running — orchestrator turn dispatched, in flight
11
+ -- done — turn finished, latest assistant message is the final answer
12
+ -- error — turn failed, last_error has the message
13
+ --
14
+ -- The partial index on status='running' keeps "find in-flight turns" cheap
15
+ -- without bloating the index for the 99% steady-state case.
16
+
17
+ ALTER TABLE orchestrator_conversations
18
+ ADD COLUMN status TEXT NOT NULL DEFAULT 'idle';
19
+
20
+ ALTER TABLE orchestrator_conversations
21
+ ADD COLUMN last_error TEXT;
22
+
23
+ CREATE INDEX IF NOT EXISTS idx_conv_status_running
24
+ ON orchestrator_conversations(workspace_id, status)
25
+ WHERE status = 'running';