let-them-talk 5.3.0 → 5.4.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/CHANGELOG.md +3 -1
- package/README.md +158 -592
- package/SECURITY.md +3 -3
- package/USAGE.md +151 -0
- package/agent-contracts.js +447 -0
- package/api-agents.js +760 -0
- package/autonomy/decision-v2.js +380 -0
- package/autonomy/watchdog-policy.js +572 -0
- package/cli.js +454 -298
- package/conversation-templates/autonomous-feature.json +83 -22
- package/conversation-templates/code-review.json +69 -21
- package/conversation-templates/debug-squad.json +69 -21
- package/conversation-templates/feature-build.json +69 -21
- package/conversation-templates/research-write.json +69 -21
- package/dashboard.html +3148 -174
- package/dashboard.js +823 -786
- package/data-dir.js +58 -0
- package/docs/architecture/branch-semantics.md +157 -0
- package/docs/architecture/canonical-event-schema.md +88 -0
- package/docs/architecture/markdown-workspace.md +183 -0
- package/docs/architecture/runtime-contract.md +459 -0
- package/docs/architecture/runtime-migration-hardening.md +64 -0
- package/events/hooks.js +154 -0
- package/events/log.js +457 -0
- package/events/replay.js +33 -0
- package/events/schema.js +432 -0
- package/managed-team-integration.js +261 -0
- package/office/agents.js +704 -597
- package/office/animation.js +1 -1
- package/office/assets/arcade-cabinet.js +141 -0
- package/office/assets/archway.js +77 -0
- package/office/assets/bar-counter.js +91 -0
- package/office/assets/bar-stool.js +71 -0
- package/office/assets/beanbag.js +64 -0
- package/office/assets/bench.js +99 -0
- package/office/assets/bollard.js +87 -0
- package/office/assets/cactus.js +100 -0
- package/office/assets/carpet-tile.js +46 -0
- package/office/assets/chair.js +123 -0
- package/office/assets/chandelier.js +107 -0
- package/office/assets/coffee-machine.js +95 -0
- package/office/assets/coffee-table.js +81 -0
- package/office/assets/column.js +95 -0
- package/office/assets/desk-lamp.js +102 -0
- package/office/assets/desk.js +76 -0
- package/office/assets/dining-table.js +105 -0
- package/office/assets/door.js +70 -0
- package/office/assets/dual-monitor.js +72 -0
- package/office/assets/fence.js +76 -0
- package/office/assets/filing-cabinet.js +111 -0
- package/office/assets/floor-lamp.js +69 -0
- package/office/assets/floor-tile.js +54 -0
- package/office/assets/flower-pot.js +76 -0
- package/office/assets/foosball.js +95 -0
- package/office/assets/fridge.js +99 -0
- package/office/assets/gaming-chair.js +154 -0
- package/office/assets/gaming-desk.js +105 -0
- package/office/assets/glass-door.js +72 -0
- package/office/assets/glass-wall.js +64 -0
- package/office/assets/half-wall.js +49 -0
- package/office/assets/hanging-plant.js +112 -0
- package/office/assets/index.js +151 -0
- package/office/assets/indoor-tree.js +90 -0
- package/office/assets/l-sofa.js +153 -0
- package/office/assets/marble-floor.js +64 -0
- package/office/assets/materials.js +40 -0
- package/office/assets/meeting-table.js +88 -0
- package/office/assets/microwave.js +94 -0
- package/office/assets/monitor.js +67 -0
- package/office/assets/neon-strip.js +73 -0
- package/office/assets/painting.js +84 -0
- package/office/assets/palm-tree.js +108 -0
- package/office/assets/pc-tower.js +91 -0
- package/office/assets/pendant-light.js +67 -0
- package/office/assets/ping-pong.js +114 -0
- package/office/assets/plant.js +72 -0
- package/office/assets/planter-box.js +95 -0
- package/office/assets/pool-table.js +94 -0
- package/office/assets/printer.js +113 -0
- package/office/assets/reception-desk.js +133 -0
- package/office/assets/rug.js +78 -0
- package/office/assets/sculpture.js +85 -0
- package/office/assets/server-rack.js +98 -0
- package/office/assets/sink.js +109 -0
- package/office/assets/sofa.js +106 -0
- package/office/assets/speaker.js +83 -0
- package/office/assets/spotlight.js +83 -0
- package/office/assets/street-lamp.js +97 -0
- package/office/assets/trash-can.js +83 -0
- package/office/assets/treadmill.js +126 -0
- package/office/assets/trophy.js +89 -0
- package/office/assets/tv-screen.js +79 -0
- package/office/assets/vase.js +84 -0
- package/office/assets/wall-clock.js +84 -0
- package/office/assets/wall.js +53 -0
- package/office/assets/water-cooler.js +146 -0
- package/office/assets/whiteboard.js +115 -0
- package/office/assets.js +3 -431
- package/office/builder.js +791 -355
- package/office/campus-env.js +1012 -1119
- package/office/environment.js +2 -0
- package/office/gallery.js +997 -0
- package/office/index.js +165 -61
- package/office/navigation.js +173 -152
- package/office/player.js +178 -68
- package/office/robot-character.js +272 -0
- package/office/spectator-camera.js +33 -10
- package/office/state.js +2 -0
- package/office/world-save.js +35 -4
- package/package.json +57 -3
- package/providers/comfyui.js +383 -0
- package/providers/dalle.js +79 -0
- package/providers/gemini.js +181 -0
- package/providers/ollama.js +184 -0
- package/providers/replicate.js +115 -0
- package/providers/zai.js +183 -0
- package/runtime-descriptor.js +270 -0
- package/scripts/check-agent-contract-advisory.js +132 -0
- package/scripts/check-api-agent-parity.js +277 -0
- package/scripts/check-autonomy-v2-decision.js +207 -0
- package/scripts/check-autonomy-v2-execution.js +588 -0
- package/scripts/check-autonomy-v2-watchdog.js +224 -0
- package/scripts/check-branch-fork-snapshot.js +337 -0
- package/scripts/check-branch-isolation.js +787 -0
- package/scripts/check-branch-semantics.js +139 -0
- package/scripts/check-dashboard-control-plane.js +1304 -0
- package/scripts/check-docs-onboarding.js +490 -0
- package/scripts/check-event-schema.js +276 -0
- package/scripts/check-evidence-completion.js +239 -0
- package/scripts/check-invariants.js +992 -0
- package/scripts/check-lifecycle-hooks.js +525 -0
- package/scripts/check-managed-team-integration.js +166 -0
- package/scripts/check-markdown-workspace-export.js +548 -0
- package/scripts/check-markdown-workspace-safety.js +347 -0
- package/scripts/check-markdown-workspace.js +136 -0
- package/scripts/check-message-replay.js +429 -0
- package/scripts/check-migration-hardening.js +300 -0
- package/scripts/check-performance-indexing.js +272 -0
- package/scripts/check-provider-capabilities.js +316 -0
- package/scripts/check-runtime-contract.js +109 -0
- package/scripts/check-session-aware-context.js +172 -0
- package/scripts/check-session-lifecycle.js +210 -0
- package/scripts/export-markdown-workspace.js +84 -0
- package/scripts/fixtures/message-replay/clean.jsonl +2 -0
- package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
- package/scripts/migrate-legacy-to-canonical.js +201 -0
- package/scripts/run-verification-suite.js +242 -0
- package/scripts/sync-packaged-docs.js +69 -0
- package/server.js +9546 -7216
- package/state/agents.js +161 -0
- package/state/canonical.js +3068 -0
- package/state/dashboard-queries.js +441 -0
- package/state/evidence.js +56 -0
- package/state/io.js +69 -0
- package/state/markdown-workspace.js +951 -0
- package/state/messages.js +669 -0
- package/state/sessions.js +683 -0
- package/state/tasks-workflows.js +92 -0
- package/templates/debate.json +2 -2
- package/templates/managed.json +4 -4
- package/templates/pair.json +2 -2
- package/templates/review.json +2 -2
- package/templates/team.json +3 -3
package/SECURITY.md
CHANGED
package/USAGE.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<!-- Generated from ../USAGE.md by scripts/sync-packaged-docs.js for published package consumers. -->
|
|
2
|
+
|
|
3
|
+
# Let Them Talk Usage Guide v5.4.0
|
|
4
|
+
|
|
5
|
+
This guide is the short operator view of the current runtime. For normative architecture details, use the docs under `docs/architecture/`.
|
|
6
|
+
|
|
7
|
+
## Install and launch
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx let-them-talk init
|
|
11
|
+
npx let-them-talk init --claude
|
|
12
|
+
npx let-them-talk init --gemini
|
|
13
|
+
npx let-them-talk init --codex
|
|
14
|
+
npx let-them-talk init --all
|
|
15
|
+
npx let-them-talk init --ollama
|
|
16
|
+
npx let-them-talk init --template <name>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
After init, use the local launcher from the project:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node .agent-bridge/launch.js
|
|
23
|
+
node .agent-bridge/launch.js --lan
|
|
24
|
+
node .agent-bridge/launch.js status
|
|
25
|
+
node .agent-bridge/launch.js msg <agent> <text>
|
|
26
|
+
node .agent-bridge/launch.js reset
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Packaged helpers still available through `npx`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx let-them-talk dashboard
|
|
33
|
+
npx let-them-talk status
|
|
34
|
+
npx let-them-talk templates
|
|
35
|
+
npx let-them-talk uninstall
|
|
36
|
+
npx let-them-talk help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Join, listen, and resume work
|
|
40
|
+
|
|
41
|
+
Recommended entry sequence for an agent:
|
|
42
|
+
|
|
43
|
+
1. Register an agent name.
|
|
44
|
+
2. Call `get_briefing()` if the project already has history, tasks, or decisions.
|
|
45
|
+
3. Call `get_guide()` if you need the current collaboration rules for the active mode.
|
|
46
|
+
|
|
47
|
+
Current loop guidance:
|
|
48
|
+
|
|
49
|
+
- Direct mode: use `listen()`.
|
|
50
|
+
- Group or managed mode: use `listen_group()`.
|
|
51
|
+
- Codex CLI compatibility path: use `listen_codex()` when the runtime instructs it.
|
|
52
|
+
- Proactive autonomy loop: use `get_work()` and finish work with `verify_and_advance()`.
|
|
53
|
+
|
|
54
|
+
## Branch, session, and evidence model
|
|
55
|
+
|
|
56
|
+
- Canonical runtime truth is event-backed under `.agent-bridge/runtime/`.
|
|
57
|
+
- Legacy JSON and JSONL files are compatibility projections during migration. They are not the authority model.
|
|
58
|
+
- The runtime contract treats branches as full-context namespaces. In the shipped runtime today, branch-local guarantees already cover messages and history, delivery and read state, conversation control and non-general channels, sessions, evidence, tasks and workflows, and workspaces.
|
|
59
|
+
- Branch-local guarantees now also cover the governance surfaces that used to remain compatibility-shared during migration: decisions, KB, reviews, dependencies, votes, rules, and progress.
|
|
60
|
+
- A branch switch changes the whole migrated branch-local collaboration view, not only message history.
|
|
61
|
+
- Sessions are scoped to one agent on one branch. Rejoining the same branch resumes that branch-scoped context, switching branches suspends one branch session and creates or resumes another, and forks carry historical session and evidence context without cloning live sessions.
|
|
62
|
+
- Completion is only authoritative when structured evidence is recorded.
|
|
63
|
+
- The current evidence payload centers on `summary`, `verification`, `files_changed`, `confidence`, `recorded_at`, and `recorded_by_session`.
|
|
64
|
+
|
|
65
|
+
## Runtime descriptors and provider capabilities
|
|
66
|
+
|
|
67
|
+
API-backed agents now persist an explicit runtime descriptor with:
|
|
68
|
+
|
|
69
|
+
- `runtime_type`
|
|
70
|
+
- `provider_id`
|
|
71
|
+
- `model_id`
|
|
72
|
+
- `capabilities`
|
|
73
|
+
|
|
74
|
+
Supported capability tokens:
|
|
75
|
+
|
|
76
|
+
- `chat`
|
|
77
|
+
- `vision`
|
|
78
|
+
- `image_generation`
|
|
79
|
+
- `video_generation`
|
|
80
|
+
- `texture_generation`
|
|
81
|
+
|
|
82
|
+
Legacy `provider`, `provider_color`, and `bot_capability` fields remain compatibility projections over the explicit descriptor.
|
|
83
|
+
|
|
84
|
+
## Templates shipped today
|
|
85
|
+
|
|
86
|
+
Agent templates:
|
|
87
|
+
|
|
88
|
+
- `pair`
|
|
89
|
+
- `team`
|
|
90
|
+
- `review`
|
|
91
|
+
- `debate`
|
|
92
|
+
- `managed`
|
|
93
|
+
|
|
94
|
+
Conversation templates:
|
|
95
|
+
|
|
96
|
+
- `autonomous-feature`
|
|
97
|
+
- `code-review`
|
|
98
|
+
- `debug-squad`
|
|
99
|
+
- `feature-build`
|
|
100
|
+
- `research-write`
|
|
101
|
+
|
|
102
|
+
## Markdown workspace export and safety
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm run export:markdown-workspace
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Current markdown workspace rules:
|
|
109
|
+
|
|
110
|
+
- default export root is `<project>/.agent-bridge-markdown/`
|
|
111
|
+
- exported files declare `authoritative: false`
|
|
112
|
+
- export is one-way only
|
|
113
|
+
- markdown edits do not write back into runtime state
|
|
114
|
+
- there is no watcher loop or implicit markdown import path
|
|
115
|
+
- compatibility-shared and main-only surfaces are exported only for `main` or omitted, never fabricated into non-main branch folders
|
|
116
|
+
|
|
117
|
+
## Verification
|
|
118
|
+
|
|
119
|
+
Package directory:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm test
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Grouped package commands:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm run verify
|
|
129
|
+
npm run verify:contracts
|
|
130
|
+
npm run verify:replay
|
|
131
|
+
npm run verify:invariants
|
|
132
|
+
npm run verify:smoke
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Grouped coverage today:
|
|
136
|
+
|
|
137
|
+
- `verify:contracts` checks the runtime contract, canonical event schema, branch semantics, and markdown workspace contract.
|
|
138
|
+
- `verify:replay` checks healthy and clean replay plus expected-failure negative replay scenarios.
|
|
139
|
+
- `verify:invariants` checks authority routing, dashboard control plane behavior, performance and indexing, provider capabilities, API-agent parity, dashboard semantic-gap coverage, migration hardening, branch isolation, session lifecycle, evidence-backed completion, session-aware context, autonomy v2, advisory contracts, managed-team integration, lifecycle hooks, and markdown workspace export and safety.
|
|
140
|
+
- `verify:smoke` runs a representative subset, including the dashboard semantic-gap check.
|
|
141
|
+
|
|
142
|
+
Coverage is still partial. The suite does not claim a full provider or runtime matrix, and it does not include browser automation.
|
|
143
|
+
|
|
144
|
+
## Source of truth references
|
|
145
|
+
|
|
146
|
+
- `README.md`
|
|
147
|
+
- `docs/architecture/runtime-contract.md`
|
|
148
|
+
- `docs/architecture/branch-semantics.md`
|
|
149
|
+
- `docs/architecture/canonical-event-schema.md`
|
|
150
|
+
- `docs/architecture/markdown-workspace.md`
|
|
151
|
+
- `docs/architecture/runtime-migration-hardening.md`
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
const VALID_AGENT_ARCHETYPES = Object.freeze([
|
|
2
|
+
'generalist',
|
|
3
|
+
'coordinator',
|
|
4
|
+
'implementer',
|
|
5
|
+
'reviewer',
|
|
6
|
+
'advisor',
|
|
7
|
+
'monitor',
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
const VALID_CONTRACT_MODES = Object.freeze(['advisory', 'strict']);
|
|
11
|
+
|
|
12
|
+
const ROLE_TOKEN_ALIASES = Object.freeze({
|
|
13
|
+
lead: 'lead',
|
|
14
|
+
manager: 'manager',
|
|
15
|
+
coordinator: 'coordinator',
|
|
16
|
+
architect: 'architect',
|
|
17
|
+
backend: 'backend',
|
|
18
|
+
frontend: 'frontend',
|
|
19
|
+
implementer: 'implementer',
|
|
20
|
+
developer: 'implementer',
|
|
21
|
+
coder: 'implementer',
|
|
22
|
+
quality: 'quality',
|
|
23
|
+
'quality lead': 'quality',
|
|
24
|
+
reviewer: 'reviewer',
|
|
25
|
+
advisor: 'advisor',
|
|
26
|
+
monitor: 'monitor',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function freezeArchetype(definition) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
label: definition.label,
|
|
32
|
+
summary: definition.summary,
|
|
33
|
+
compatible_roles: Object.freeze([...(definition.compatible_roles || [])]),
|
|
34
|
+
default_skills: Object.freeze([...(definition.default_skills || [])]),
|
|
35
|
+
keywords: Object.freeze([...(definition.keywords || [])]),
|
|
36
|
+
preferred_work_types: Object.freeze([...(definition.preferred_work_types || [])]),
|
|
37
|
+
discouraged_work_types: Object.freeze([...(definition.discouraged_work_types || [])]),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const AGENT_ARCHETYPE_REGISTRY = Object.freeze({
|
|
42
|
+
generalist: freezeArchetype({
|
|
43
|
+
label: 'Generalist',
|
|
44
|
+
summary: 'Flexible contributor who can work across planning, implementation, review, and coordination as needed.',
|
|
45
|
+
compatible_roles: ['lead', 'manager', 'coordinator', 'architect', 'backend', 'frontend', 'implementer', 'quality', 'reviewer', 'advisor', 'monitor'],
|
|
46
|
+
default_skills: [],
|
|
47
|
+
keywords: ['general', 'support'],
|
|
48
|
+
preferred_work_types: ['workflow_step', 'claimed_task', 'task', 'review', 'help_teammate', 'unblock', 'prep_work', 'messages', 'team_coordination', 'managed_manager'],
|
|
49
|
+
discouraged_work_types: [],
|
|
50
|
+
}),
|
|
51
|
+
coordinator: freezeArchetype({
|
|
52
|
+
label: 'Coordinator',
|
|
53
|
+
summary: 'Best at planning, triage, delegation, and keeping the team moving.',
|
|
54
|
+
compatible_roles: ['lead', 'manager', 'coordinator'],
|
|
55
|
+
default_skills: ['planning', 'coordination', 'delegation'],
|
|
56
|
+
keywords: ['plan', 'planning', 'coordinate', 'coordination', 'delegate', 'workflow', 'triage'],
|
|
57
|
+
preferred_work_types: ['messages', 'help_teammate', 'unblock', 'prep_work', 'review', 'team_coordination', 'managed_manager'],
|
|
58
|
+
discouraged_work_types: [],
|
|
59
|
+
}),
|
|
60
|
+
implementer: freezeArchetype({
|
|
61
|
+
label: 'Implementer',
|
|
62
|
+
summary: 'Best at building, fixing, and shipping concrete code changes.',
|
|
63
|
+
compatible_roles: ['architect', 'backend', 'frontend', 'implementer'],
|
|
64
|
+
default_skills: ['implementation', 'backend', 'frontend', 'testing'],
|
|
65
|
+
keywords: ['implement', 'implementation', 'build', 'fix', 'code', 'refactor', 'backend', 'frontend', 'api', 'ui', 'bug', 'test'],
|
|
66
|
+
preferred_work_types: ['workflow_step', 'claimed_task', 'task', 'prep_work', 'help_teammate'],
|
|
67
|
+
discouraged_work_types: [],
|
|
68
|
+
}),
|
|
69
|
+
reviewer: freezeArchetype({
|
|
70
|
+
label: 'Reviewer',
|
|
71
|
+
summary: 'Best at review, verification, test emphasis, and defect finding.',
|
|
72
|
+
compatible_roles: ['quality', 'reviewer'],
|
|
73
|
+
default_skills: ['review', 'testing', 'verification'],
|
|
74
|
+
keywords: ['review', 'verify', 'verification', 'test', 'testing', 'qa', 'quality', 'bug', 'security'],
|
|
75
|
+
preferred_work_types: ['review', 'unblock', 'help_teammate'],
|
|
76
|
+
discouraged_work_types: [],
|
|
77
|
+
}),
|
|
78
|
+
advisor: freezeArchetype({
|
|
79
|
+
label: 'Advisor',
|
|
80
|
+
summary: 'Best at analysis, architecture, strategy, and guidance rather than task claiming.',
|
|
81
|
+
compatible_roles: ['advisor'],
|
|
82
|
+
default_skills: ['analysis', 'architecture', 'strategy'],
|
|
83
|
+
keywords: ['analysis', 'architecture', 'strategy', 'design', 'research', 'plan'],
|
|
84
|
+
preferred_work_types: ['messages', 'help_teammate', 'prep_work', 'unblock', 'advisor_context'],
|
|
85
|
+
discouraged_work_types: ['workflow_step', 'claimed_task', 'task', 'team_coordination', 'managed_manager'],
|
|
86
|
+
}),
|
|
87
|
+
monitor: freezeArchetype({
|
|
88
|
+
label: 'Monitor',
|
|
89
|
+
summary: 'Best at health checks, triage, queue balancing, and unblocking the team.',
|
|
90
|
+
compatible_roles: ['monitor'],
|
|
91
|
+
default_skills: ['monitoring', 'triage', 'coordination'],
|
|
92
|
+
keywords: ['monitor', 'health', 'triage', 'stuck', 'queue', 'rebalance', 'intervention'],
|
|
93
|
+
preferred_work_types: ['messages', 'help_teammate', 'unblock', 'idle', 'monitor_report'],
|
|
94
|
+
discouraged_work_types: ['workflow_step', 'claimed_task', 'task', 'team_coordination', 'managed_manager'],
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const VALID_ARCHETYPE_SET = new Set(VALID_AGENT_ARCHETYPES);
|
|
99
|
+
const VALID_CONTRACT_MODE_SET = new Set(VALID_CONTRACT_MODES);
|
|
100
|
+
const KNOWN_ROLE_TOKENS = new Set(Object.values(ROLE_TOKEN_ALIASES));
|
|
101
|
+
|
|
102
|
+
function normalizeText(value) {
|
|
103
|
+
if (typeof value !== 'string') return null;
|
|
104
|
+
const trimmed = value.trim();
|
|
105
|
+
return trimmed ? trimmed : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function normalizeLowerToken(value) {
|
|
109
|
+
const text = normalizeText(value);
|
|
110
|
+
if (!text) return null;
|
|
111
|
+
return text.toLowerCase().replace(/[_-]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function normalizeRoleToken(value) {
|
|
115
|
+
const normalized = normalizeLowerToken(value);
|
|
116
|
+
if (!normalized) return null;
|
|
117
|
+
if (ROLE_TOKEN_ALIASES[normalized]) return ROLE_TOKEN_ALIASES[normalized];
|
|
118
|
+
if (normalized.indexOf('implementer') === 0) return 'implementer';
|
|
119
|
+
return normalized;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeArchetype(value) {
|
|
123
|
+
const normalized = normalizeLowerToken(value);
|
|
124
|
+
if (!normalized) return null;
|
|
125
|
+
return VALID_ARCHETYPE_SET.has(normalized) ? normalized : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function normalizeContractMode(value) {
|
|
129
|
+
const normalized = normalizeLowerToken(value);
|
|
130
|
+
if (!normalized) return null;
|
|
131
|
+
return VALID_CONTRACT_MODE_SET.has(normalized) ? normalized : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function normalizeSkillToken(value) {
|
|
135
|
+
return normalizeLowerToken(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function normalizeWorkType(value) {
|
|
139
|
+
const text = normalizeText(value);
|
|
140
|
+
if (!text) return null;
|
|
141
|
+
return text.toLowerCase().replace(/[\s-]+/g, '_');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeSkills(value) {
|
|
145
|
+
const entries = Array.isArray(value)
|
|
146
|
+
? value
|
|
147
|
+
: (value == null ? [] : [value]);
|
|
148
|
+
const normalized = [];
|
|
149
|
+
const seen = new Set();
|
|
150
|
+
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
const token = normalizeSkillToken(entry);
|
|
153
|
+
if (!token || seen.has(token)) continue;
|
|
154
|
+
seen.add(token);
|
|
155
|
+
normalized.push(token);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return normalized;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function mergeSkills() {
|
|
162
|
+
const merged = [];
|
|
163
|
+
const seen = new Set();
|
|
164
|
+
|
|
165
|
+
for (const list of arguments) {
|
|
166
|
+
for (const entry of Array.isArray(list) ? list : []) {
|
|
167
|
+
const token = normalizeSkillToken(entry);
|
|
168
|
+
if (!token || seen.has(token)) continue;
|
|
169
|
+
seen.add(token);
|
|
170
|
+
merged.push(token);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return merged;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function inferArchetypeFromRole(roleToken) {
|
|
178
|
+
if (!roleToken) return null;
|
|
179
|
+
const orderedArchetypes = ['coordinator', 'implementer', 'reviewer', 'advisor', 'monitor', 'generalist'];
|
|
180
|
+
for (const archetype of orderedArchetypes) {
|
|
181
|
+
const definition = AGENT_ARCHETYPE_REGISTRY[archetype];
|
|
182
|
+
if (definition.compatible_roles.includes(roleToken)) return archetype;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function createDefaultContractMetadata() {
|
|
188
|
+
return {
|
|
189
|
+
skills: [],
|
|
190
|
+
contract_mode: 'advisory',
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function sanitizeContractProfilePatch(input = {}) {
|
|
195
|
+
const errors = [];
|
|
196
|
+
const normalized = {};
|
|
197
|
+
|
|
198
|
+
if (Object.prototype.hasOwnProperty.call(input, 'archetype')) {
|
|
199
|
+
if (input.archetype == null || normalizeText(input.archetype) == null) {
|
|
200
|
+
normalized.archetype = null;
|
|
201
|
+
} else {
|
|
202
|
+
const archetype = normalizeArchetype(input.archetype);
|
|
203
|
+
if (!archetype) {
|
|
204
|
+
errors.push(`archetype must be one of: ${VALID_AGENT_ARCHETYPES.join(', ')}`);
|
|
205
|
+
} else {
|
|
206
|
+
normalized.archetype = archetype;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (Object.prototype.hasOwnProperty.call(input, 'skills')) {
|
|
212
|
+
if (input.skills == null) {
|
|
213
|
+
normalized.skills = [];
|
|
214
|
+
} else if (!Array.isArray(input.skills) && typeof input.skills !== 'string') {
|
|
215
|
+
errors.push('skills must be a string or an array of strings');
|
|
216
|
+
} else if (typeof input.skills === 'string' && normalizeText(input.skills) == null) {
|
|
217
|
+
normalized.skills = [];
|
|
218
|
+
} else {
|
|
219
|
+
normalized.skills = normalizeSkills(input.skills);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (Object.prototype.hasOwnProperty.call(input, 'contract_mode')) {
|
|
224
|
+
if (input.contract_mode == null || normalizeText(input.contract_mode) == null) {
|
|
225
|
+
normalized.contract_mode = 'advisory';
|
|
226
|
+
} else {
|
|
227
|
+
const contractMode = normalizeContractMode(input.contract_mode);
|
|
228
|
+
if (!contractMode) {
|
|
229
|
+
errors.push(`contract_mode must be one of: ${VALID_CONTRACT_MODES.join(', ')}`);
|
|
230
|
+
} else {
|
|
231
|
+
normalized.contract_mode = contractMode;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
valid: errors.length === 0,
|
|
238
|
+
errors,
|
|
239
|
+
normalized,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function resolveAgentContract(profile = {}) {
|
|
244
|
+
const rawRole = normalizeText(profile.role) || '';
|
|
245
|
+
const roleToken = normalizeRoleToken(profile.role);
|
|
246
|
+
const roleRecognized = roleToken ? KNOWN_ROLE_TOKENS.has(roleToken) : false;
|
|
247
|
+
const declaredArchetype = normalizeArchetype(profile.archetype);
|
|
248
|
+
const resolvedArchetype = declaredArchetype || inferArchetypeFromRole(roleToken);
|
|
249
|
+
const declaredSkills = normalizeSkills(profile.skills);
|
|
250
|
+
const registryEntry = resolvedArchetype ? AGENT_ARCHETYPE_REGISTRY[resolvedArchetype] : null;
|
|
251
|
+
const effectiveSkills = mergeSkills(declaredSkills, registryEntry ? registryEntry.default_skills : []);
|
|
252
|
+
const contractMode = normalizeContractMode(profile.contract_mode) || 'advisory';
|
|
253
|
+
const explicitMode = normalizeContractMode(profile.contract_mode);
|
|
254
|
+
|
|
255
|
+
let roleAlignment = 'none';
|
|
256
|
+
if (declaredArchetype && roleToken) {
|
|
257
|
+
if (registryEntry && registryEntry.compatible_roles.includes(roleToken)) {
|
|
258
|
+
roleAlignment = 'aligned';
|
|
259
|
+
} else if (roleRecognized) {
|
|
260
|
+
roleAlignment = 'mismatch';
|
|
261
|
+
} else {
|
|
262
|
+
roleAlignment = 'unknown_role';
|
|
263
|
+
}
|
|
264
|
+
} else if (declaredArchetype) {
|
|
265
|
+
roleAlignment = 'archetype_only';
|
|
266
|
+
} else if (roleToken) {
|
|
267
|
+
roleAlignment = 'legacy_role_only';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const hasExplicitContract = !!(
|
|
271
|
+
declaredArchetype
|
|
272
|
+
|| declaredSkills.length > 0
|
|
273
|
+
|| (explicitMode && explicitMode !== 'advisory')
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
role: rawRole,
|
|
278
|
+
role_token: roleToken,
|
|
279
|
+
role_recognized: roleRecognized,
|
|
280
|
+
declared_archetype: declaredArchetype,
|
|
281
|
+
archetype: resolvedArchetype,
|
|
282
|
+
skills: declaredSkills,
|
|
283
|
+
effective_skills: effectiveSkills,
|
|
284
|
+
contract_mode: contractMode,
|
|
285
|
+
role_alignment: roleAlignment,
|
|
286
|
+
has_explicit_contract: hasExplicitContract,
|
|
287
|
+
has_advisory_context: hasExplicitContract || !!roleToken,
|
|
288
|
+
registry_entry: registryEntry,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function buildGuideContractAdvisory(contract) {
|
|
293
|
+
if (!contract || !contract.has_advisory_context) return null;
|
|
294
|
+
|
|
295
|
+
let status = 'legacy';
|
|
296
|
+
let summary;
|
|
297
|
+
|
|
298
|
+
if (contract.declared_archetype && contract.role_alignment === 'aligned') {
|
|
299
|
+
status = 'aligned';
|
|
300
|
+
summary = `Declared archetype "${contract.declared_archetype}" aligns with role "${contract.role || contract.role_token}".`;
|
|
301
|
+
} else if (contract.declared_archetype && contract.role_alignment === 'mismatch') {
|
|
302
|
+
status = 'mismatch';
|
|
303
|
+
summary = `Declared archetype "${contract.declared_archetype}" may be a weaker fit for role "${contract.role || contract.role_token}".`;
|
|
304
|
+
} else if (contract.declared_archetype && contract.role_alignment === 'unknown_role') {
|
|
305
|
+
status = 'legacy';
|
|
306
|
+
summary = `Declared archetype "${contract.declared_archetype}" is active, while role "${contract.role}" remains a compatibility-only free-form label.`;
|
|
307
|
+
} else if (contract.declared_archetype) {
|
|
308
|
+
status = 'aligned';
|
|
309
|
+
summary = `Declared archetype "${contract.declared_archetype}" is active in ${contract.contract_mode} mode.`;
|
|
310
|
+
} else {
|
|
311
|
+
summary = contract.role
|
|
312
|
+
? `Legacy role "${contract.role}" remains active during contract migration.`
|
|
313
|
+
: `Contract mode is ${contract.contract_mode}.`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const recommendation = contract.registry_entry
|
|
317
|
+
? contract.registry_entry.summary
|
|
318
|
+
: 'Add an archetype and skills[] if you want more explicit advisory guidance.';
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
status,
|
|
322
|
+
summary,
|
|
323
|
+
recommendation,
|
|
324
|
+
migration_note: contract.contract_mode === 'strict'
|
|
325
|
+
? 'Strict intent is recorded, but this Task 11B slice keeps contract handling advisory outside existing managed-mode and evidence gates.'
|
|
326
|
+
: null,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function collectFitKeywords(contract, text) {
|
|
331
|
+
if (!contract || !text) return [];
|
|
332
|
+
|
|
333
|
+
const haystack = String(text).toLowerCase();
|
|
334
|
+
const candidates = mergeSkills(
|
|
335
|
+
contract.skills,
|
|
336
|
+
contract.effective_skills,
|
|
337
|
+
contract.registry_entry ? contract.registry_entry.keywords : []
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const matches = [];
|
|
341
|
+
for (const token of candidates) {
|
|
342
|
+
if (token.length < 3) continue;
|
|
343
|
+
if (haystack.includes(token) && !matches.includes(token)) matches.push(token);
|
|
344
|
+
if (matches.length >= 4) break;
|
|
345
|
+
}
|
|
346
|
+
return matches;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function analyzeContractFit(contract, target = {}) {
|
|
350
|
+
if (!contract || !contract.has_advisory_context) return null;
|
|
351
|
+
|
|
352
|
+
const workType = normalizeWorkType(target.work_type) || 'task';
|
|
353
|
+
const title = normalizeText(target.title) || '';
|
|
354
|
+
const description = normalizeText(target.description) || '';
|
|
355
|
+
const text = `${title} ${description}`.trim();
|
|
356
|
+
const keywordMatches = collectFitKeywords(contract, text);
|
|
357
|
+
const registryEntry = contract.registry_entry;
|
|
358
|
+
const assigned = !!target.assigned;
|
|
359
|
+
let score = 0;
|
|
360
|
+
|
|
361
|
+
if (registryEntry && registryEntry.preferred_work_types.includes(workType)) score += 2;
|
|
362
|
+
if (registryEntry && registryEntry.discouraged_work_types.includes(workType)) score -= 2;
|
|
363
|
+
if (keywordMatches.length > 0) score += keywordMatches.length >= 2 ? 2 : 1;
|
|
364
|
+
|
|
365
|
+
let status = 'neutral';
|
|
366
|
+
if (score >= 2) status = 'aligned';
|
|
367
|
+
else if (score === 1) status = 'partial';
|
|
368
|
+
else if (score < 0) status = 'mismatch';
|
|
369
|
+
|
|
370
|
+
let summary;
|
|
371
|
+
if (status === 'aligned') {
|
|
372
|
+
summary = contract.archetype
|
|
373
|
+
? `This ${workType.replace(/_/g, ' ')} fits your ${contract.archetype} contract.`
|
|
374
|
+
: 'This work fits your current legacy role guidance.';
|
|
375
|
+
} else if (status === 'partial') {
|
|
376
|
+
summary = contract.archetype
|
|
377
|
+
? `This ${workType.replace(/_/g, ' ')} partially fits your ${contract.archetype} contract.`
|
|
378
|
+
: 'This work partially fits your current legacy role guidance.';
|
|
379
|
+
} else if (status === 'mismatch' && assigned) {
|
|
380
|
+
summary = contract.archetype
|
|
381
|
+
? `This assigned work may be a weaker fit for your ${contract.archetype} contract, but assigned work still takes precedence in this advisory slice.`
|
|
382
|
+
: 'This assigned work may be a weaker fit for your current legacy role guidance, but assigned work still takes precedence in this advisory slice.';
|
|
383
|
+
} else if (status === 'mismatch') {
|
|
384
|
+
summary = contract.archetype
|
|
385
|
+
? `This ${workType.replace(/_/g, ' ')} may be a weaker fit for your ${contract.archetype} contract.`
|
|
386
|
+
: 'This work may be a weaker fit for your current legacy role guidance.';
|
|
387
|
+
} else {
|
|
388
|
+
summary = contract.archetype
|
|
389
|
+
? `No strong contract fit signal for this ${workType.replace(/_/g, ' ')}.`
|
|
390
|
+
: 'No strong contract fit signal for this work item.';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
status,
|
|
395
|
+
summary,
|
|
396
|
+
keyword_matches: keywordMatches,
|
|
397
|
+
recommendation: registryEntry ? registryEntry.summary : null,
|
|
398
|
+
migration_note: contract.contract_mode === 'strict' && status !== 'aligned'
|
|
399
|
+
? 'Strict intent is recorded, but Task 11B surfaces this only as advisory guidance.'
|
|
400
|
+
: null,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function buildRuntimeContractMetadata(contract) {
|
|
405
|
+
if (!contract) {
|
|
406
|
+
return {
|
|
407
|
+
archetype: '',
|
|
408
|
+
skills: [],
|
|
409
|
+
contract_mode: 'advisory',
|
|
410
|
+
has_explicit_contract: false,
|
|
411
|
+
contract: null,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
archetype: contract.declared_archetype || '',
|
|
417
|
+
skills: contract.skills,
|
|
418
|
+
contract_mode: contract.contract_mode,
|
|
419
|
+
has_explicit_contract: contract.has_explicit_contract,
|
|
420
|
+
contract: {
|
|
421
|
+
archetype: contract.archetype || null,
|
|
422
|
+
declared_archetype: contract.declared_archetype || null,
|
|
423
|
+
role_token: contract.role_token || null,
|
|
424
|
+
role_alignment: contract.role_alignment,
|
|
425
|
+
skills: contract.skills,
|
|
426
|
+
effective_skills: contract.effective_skills,
|
|
427
|
+
contract_mode: contract.contract_mode,
|
|
428
|
+
has_explicit_contract: contract.has_explicit_contract,
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
module.exports = {
|
|
434
|
+
AGENT_ARCHETYPE_REGISTRY,
|
|
435
|
+
VALID_AGENT_ARCHETYPES,
|
|
436
|
+
VALID_CONTRACT_MODES,
|
|
437
|
+
analyzeContractFit,
|
|
438
|
+
buildGuideContractAdvisory,
|
|
439
|
+
buildRuntimeContractMetadata,
|
|
440
|
+
createDefaultContractMetadata,
|
|
441
|
+
normalizeArchetype,
|
|
442
|
+
normalizeContractMode,
|
|
443
|
+
normalizeRoleToken,
|
|
444
|
+
normalizeSkills,
|
|
445
|
+
resolveAgentContract,
|
|
446
|
+
sanitizeContractProfilePatch,
|
|
447
|
+
};
|