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
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
const { analyzeContractFit } = require('../agent-contracts');
|
|
2
|
+
const { resolveAgentRuntimeMetadata, VALID_CAPABILITIES } = require('../runtime-descriptor');
|
|
3
|
+
|
|
4
|
+
const VALID_CAPABILITY_SET = new Set(VALID_CAPABILITIES);
|
|
5
|
+
|
|
6
|
+
const WORK_TYPE_BASE_SCORES = Object.freeze({
|
|
7
|
+
workflow_step: 100,
|
|
8
|
+
messages: 90,
|
|
9
|
+
task: 60,
|
|
10
|
+
help_teammate: 55,
|
|
11
|
+
review: 54,
|
|
12
|
+
unblock: 50,
|
|
13
|
+
stolen_task: 48,
|
|
14
|
+
prep_work: 30,
|
|
15
|
+
idle: 0,
|
|
16
|
+
advisor_context: 95,
|
|
17
|
+
monitor_report: 95,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const ASSIGNMENT_PRIORITIES = Object.freeze({
|
|
21
|
+
none: 0,
|
|
22
|
+
assigned: 1,
|
|
23
|
+
active: 2,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function normalizeText(value) {
|
|
27
|
+
if (typeof value !== 'string') return null;
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
return trimmed ? trimmed : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeCapabilityList(value) {
|
|
33
|
+
const entries = Array.isArray(value)
|
|
34
|
+
? value
|
|
35
|
+
: (value == null ? [] : [value]);
|
|
36
|
+
const normalized = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const text = normalizeText(entry);
|
|
41
|
+
if (!text) continue;
|
|
42
|
+
const token = text.toLowerCase();
|
|
43
|
+
if (!VALID_CAPABILITY_SET.has(token) || seen.has(token)) continue;
|
|
44
|
+
seen.add(token);
|
|
45
|
+
normalized.push(token);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return normalized;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveAssignmentPriority(target = {}) {
|
|
52
|
+
if (target.assignment_priority && Object.prototype.hasOwnProperty.call(ASSIGNMENT_PRIORITIES, target.assignment_priority)) {
|
|
53
|
+
return ASSIGNMENT_PRIORITIES[target.assignment_priority];
|
|
54
|
+
}
|
|
55
|
+
return target.assigned ? ASSIGNMENT_PRIORITIES.assigned : ASSIGNMENT_PRIORITIES.none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolveWorkType(target = {}) {
|
|
59
|
+
const workType = normalizeText(target.work_type);
|
|
60
|
+
if (!workType) return 'task';
|
|
61
|
+
return workType.toLowerCase().replace(/[\s-]+/g, '_');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resolveTargetScore(target = {}) {
|
|
65
|
+
if (Number.isFinite(target.base_score)) return target.base_score;
|
|
66
|
+
return WORK_TYPE_BASE_SCORES[resolveWorkType(target)] || 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function resolveAgentDecisionContext(params = {}) {
|
|
70
|
+
const agentRecord = params.agentRecord && typeof params.agentRecord === 'object'
|
|
71
|
+
? params.agentRecord
|
|
72
|
+
: {};
|
|
73
|
+
const runtimeMetadata = resolveAgentRuntimeMetadata({
|
|
74
|
+
...agentRecord,
|
|
75
|
+
name: params.agentName,
|
|
76
|
+
is_api_agent: !!agentRecord.is_api_agent,
|
|
77
|
+
});
|
|
78
|
+
const capabilities = Array.isArray(runtimeMetadata.capabilities) && runtimeMetadata.capabilities.length > 0
|
|
79
|
+
? runtimeMetadata.capabilities
|
|
80
|
+
: ['chat'];
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
agent_name: params.agentName || null,
|
|
84
|
+
branch_id: params.branchId || 'main',
|
|
85
|
+
session_summary: params.sessionSummary || null,
|
|
86
|
+
contract: params.contract || null,
|
|
87
|
+
available_skills: Array.isArray(params.availableSkills) ? [...params.availableSkills] : [],
|
|
88
|
+
runtime: {
|
|
89
|
+
runtime_type: runtimeMetadata.runtime_type || (agentRecord.is_api_agent ? 'api' : 'cli'),
|
|
90
|
+
provider_id: runtimeMetadata.provider_id || null,
|
|
91
|
+
model_id: runtimeMetadata.model_id || null,
|
|
92
|
+
provider: runtimeMetadata.provider || null,
|
|
93
|
+
capabilities,
|
|
94
|
+
bot_capability: runtimeMetadata.bot_capability || null,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function collectCapabilityRequirements(target = {}) {
|
|
100
|
+
return {
|
|
101
|
+
required_capabilities: normalizeCapabilityList(
|
|
102
|
+
target.required_capabilities || target.requiredCapabilities || target.capability_requirements || []
|
|
103
|
+
),
|
|
104
|
+
preferred_capabilities: normalizeCapabilityList(
|
|
105
|
+
target.preferred_capabilities || target.preferredCapabilities || []
|
|
106
|
+
),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function analyzeCapabilityFit(runtime, target = {}) {
|
|
111
|
+
const normalizedRuntime = runtime && typeof runtime === 'object'
|
|
112
|
+
? {
|
|
113
|
+
runtime_type: runtime.runtime_type || 'cli',
|
|
114
|
+
provider_id: runtime.provider_id || null,
|
|
115
|
+
model_id: runtime.model_id || null,
|
|
116
|
+
capabilities: normalizeCapabilityList(runtime.capabilities),
|
|
117
|
+
bot_capability: runtime.bot_capability || null,
|
|
118
|
+
}
|
|
119
|
+
: {
|
|
120
|
+
runtime_type: 'cli',
|
|
121
|
+
provider_id: null,
|
|
122
|
+
model_id: null,
|
|
123
|
+
capabilities: ['chat'],
|
|
124
|
+
bot_capability: null,
|
|
125
|
+
};
|
|
126
|
+
if (normalizedRuntime.capabilities.length === 0) normalizedRuntime.capabilities = ['chat'];
|
|
127
|
+
|
|
128
|
+
const requirements = collectCapabilityRequirements(target);
|
|
129
|
+
const matchedRequired = requirements.required_capabilities.filter((capability) => normalizedRuntime.capabilities.includes(capability));
|
|
130
|
+
const missingRequired = requirements.required_capabilities.filter((capability) => !normalizedRuntime.capabilities.includes(capability));
|
|
131
|
+
const matchedPreferred = requirements.preferred_capabilities.filter((capability) => normalizedRuntime.capabilities.includes(capability));
|
|
132
|
+
const missingPreferred = requirements.preferred_capabilities.filter((capability) => !normalizedRuntime.capabilities.includes(capability));
|
|
133
|
+
|
|
134
|
+
let status = 'neutral';
|
|
135
|
+
let admissible = true;
|
|
136
|
+
let summary = 'No explicit capability requirement shaped this decision.';
|
|
137
|
+
|
|
138
|
+
if (missingRequired.length > 0) {
|
|
139
|
+
if (target.assigned) {
|
|
140
|
+
status = 'mismatch';
|
|
141
|
+
summary = `This assigned work asks for ${missingRequired.join(', ')}, which your runtime does not currently advertise, but assigned work still takes precedence in this decision-only slice.`;
|
|
142
|
+
} else {
|
|
143
|
+
status = 'blocked';
|
|
144
|
+
admissible = false;
|
|
145
|
+
summary = `This work asks for ${missingRequired.join(', ')}, which your runtime does not currently advertise.`;
|
|
146
|
+
}
|
|
147
|
+
} else if (requirements.required_capabilities.length > 0) {
|
|
148
|
+
status = 'aligned';
|
|
149
|
+
summary = `Your runtime advertises the required capability set: ${requirements.required_capabilities.join(', ')}.`;
|
|
150
|
+
} else if (requirements.preferred_capabilities.length > 0) {
|
|
151
|
+
status = missingPreferred.length === 0 ? 'aligned' : 'partial';
|
|
152
|
+
summary = missingPreferred.length === 0
|
|
153
|
+
? `Your runtime advertises the preferred capability set: ${requirements.preferred_capabilities.join(', ')}.`
|
|
154
|
+
: `This work prefers ${missingPreferred.join(', ')}, which your runtime does not currently advertise.`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
status,
|
|
159
|
+
admissible,
|
|
160
|
+
summary,
|
|
161
|
+
runtime_type: normalizedRuntime.runtime_type,
|
|
162
|
+
provider_id: normalizedRuntime.provider_id,
|
|
163
|
+
model_id: normalizedRuntime.model_id,
|
|
164
|
+
runtime_capabilities: normalizedRuntime.capabilities,
|
|
165
|
+
required_capabilities: requirements.required_capabilities,
|
|
166
|
+
preferred_capabilities: requirements.preferred_capabilities,
|
|
167
|
+
matched_required: matchedRequired,
|
|
168
|
+
matched_preferred: matchedPreferred,
|
|
169
|
+
missing_required: missingRequired,
|
|
170
|
+
missing_preferred: missingPreferred,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function countResumeSignals(target = {}) {
|
|
175
|
+
let count = 0;
|
|
176
|
+
|
|
177
|
+
if (target.session_summary && target.session_summary.session_id) count += 1;
|
|
178
|
+
|
|
179
|
+
const resumeContext = target.resume_context && typeof target.resume_context === 'object'
|
|
180
|
+
? target.resume_context
|
|
181
|
+
: null;
|
|
182
|
+
if (!resumeContext) return count;
|
|
183
|
+
|
|
184
|
+
if (Array.isArray(resumeContext.dependency_evidence)) count += resumeContext.dependency_evidence.length;
|
|
185
|
+
if (Array.isArray(resumeContext.recent_evidence)) count += resumeContext.recent_evidence.length;
|
|
186
|
+
if (Array.isArray(resumeContext.message_handoffs)) count += resumeContext.message_handoffs.length;
|
|
187
|
+
|
|
188
|
+
return count;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getContractScore(advisory) {
|
|
192
|
+
if (!advisory) return 0;
|
|
193
|
+
if (advisory.status === 'aligned') return 20;
|
|
194
|
+
if (advisory.status === 'partial') return 8;
|
|
195
|
+
if (advisory.status === 'mismatch') return -20;
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function getCapabilityScore(advisory) {
|
|
200
|
+
if (!advisory) return 0;
|
|
201
|
+
if (advisory.status === 'aligned') return advisory.required_capabilities.length > 0 ? 15 : 10;
|
|
202
|
+
if (advisory.status === 'partial') return 4;
|
|
203
|
+
if (advisory.status === 'mismatch') return -5;
|
|
204
|
+
if (advisory.status === 'blocked') return -1000;
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function analyzeContractAdmissibility(contract, target = {}, advisory = null) {
|
|
209
|
+
const assigned = resolveAssignmentPriority(target) > ASSIGNMENT_PRIORITIES.none;
|
|
210
|
+
const status = advisory && advisory.status ? advisory.status : 'neutral';
|
|
211
|
+
|
|
212
|
+
if (!contract || !contract.has_explicit_contract || contract.contract_mode !== 'strict') {
|
|
213
|
+
return {
|
|
214
|
+
admissible: true,
|
|
215
|
+
status,
|
|
216
|
+
reason: 'advisory_only',
|
|
217
|
+
summary: 'Contract fit remains advisory for this decision context.',
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!assigned && status !== 'aligned' && status !== 'partial') {
|
|
222
|
+
return {
|
|
223
|
+
admissible: false,
|
|
224
|
+
status,
|
|
225
|
+
reason: status === 'mismatch' ? 'strict_contract_mismatch' : 'strict_contract_no_positive_fit',
|
|
226
|
+
summary: status === 'mismatch'
|
|
227
|
+
? 'Strict contract mode blocks claiming new work that mismatches the explicit contract.'
|
|
228
|
+
: 'Strict contract mode blocks claiming new work unless the explicit contract provides a positive fit signal.',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
admissible: true,
|
|
234
|
+
status,
|
|
235
|
+
reason: assigned ? 'assigned_precedence' : 'strict_contract_aligned',
|
|
236
|
+
summary: assigned
|
|
237
|
+
? 'Assigned work keeps precedence even under strict contract mode.'
|
|
238
|
+
: 'Strict contract mode allows this work item.',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function getAffinityScore(target = {}) {
|
|
243
|
+
if (!Number.isFinite(target.affinity_score)) return 0;
|
|
244
|
+
return Math.max(-20, Math.min(20, target.affinity_score));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function evaluateAutonomyCandidate(candidate, context = {}) {
|
|
248
|
+
const normalizedCandidate = candidate && typeof candidate === 'object' ? candidate : {};
|
|
249
|
+
const target = normalizedCandidate.target && typeof normalizedCandidate.target === 'object'
|
|
250
|
+
? normalizedCandidate.target
|
|
251
|
+
: normalizedCandidate;
|
|
252
|
+
const contractAdvisory = analyzeContractFit(context.contract, target);
|
|
253
|
+
const contractAdmissibility = analyzeContractAdmissibility(context.contract, target, contractAdvisory);
|
|
254
|
+
const capabilityAdvisory = analyzeCapabilityFit(context.runtime, target);
|
|
255
|
+
const resumeSignalCount = countResumeSignals(target);
|
|
256
|
+
const sessionMatch = !!(
|
|
257
|
+
target.session_summary
|
|
258
|
+
&& target.session_summary.session_id
|
|
259
|
+
&& context.session_summary
|
|
260
|
+
&& context.session_summary.session_id
|
|
261
|
+
&& target.session_summary.session_id === context.session_summary.session_id
|
|
262
|
+
);
|
|
263
|
+
const score = resolveTargetScore(target)
|
|
264
|
+
+ getContractScore(contractAdvisory)
|
|
265
|
+
+ getCapabilityScore(capabilityAdvisory)
|
|
266
|
+
+ getAffinityScore(target)
|
|
267
|
+
+ Math.min(resumeSignalCount, 6) * 2
|
|
268
|
+
+ (sessionMatch ? 3 : 0);
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
admissible: capabilityAdvisory.admissible && contractAdmissibility.admissible,
|
|
272
|
+
assigned_priority: resolveAssignmentPriority(target),
|
|
273
|
+
base_score: resolveTargetScore(target),
|
|
274
|
+
score,
|
|
275
|
+
session_match: sessionMatch,
|
|
276
|
+
resume_signal_count: resumeSignalCount,
|
|
277
|
+
work_type: resolveWorkType(target),
|
|
278
|
+
contract_advisory: contractAdvisory,
|
|
279
|
+
contract_admissibility: contractAdmissibility,
|
|
280
|
+
capability_advisory: capabilityAdvisory,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function compareEvaluatedCandidates(left, right) {
|
|
285
|
+
const leftEvaluation = left.evaluation || {};
|
|
286
|
+
const rightEvaluation = right.evaluation || {};
|
|
287
|
+
|
|
288
|
+
if ((rightEvaluation.assigned_priority || 0) !== (leftEvaluation.assigned_priority || 0)) {
|
|
289
|
+
return (rightEvaluation.assigned_priority || 0) - (leftEvaluation.assigned_priority || 0);
|
|
290
|
+
}
|
|
291
|
+
if ((rightEvaluation.score || 0) !== (leftEvaluation.score || 0)) {
|
|
292
|
+
return (rightEvaluation.score || 0) - (leftEvaluation.score || 0);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const leftOrder = Number.isFinite(left.order) ? left.order : Number.MAX_SAFE_INTEGER;
|
|
296
|
+
const rightOrder = Number.isFinite(right.order) ? right.order : Number.MAX_SAFE_INTEGER;
|
|
297
|
+
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
298
|
+
|
|
299
|
+
const leftId = normalizeText(left.id) || '';
|
|
300
|
+
const rightId = normalizeText(right.id) || '';
|
|
301
|
+
return leftId.localeCompare(rightId);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function selectAutonomyDecisionCandidate(candidates, context = {}) {
|
|
305
|
+
return (Array.isArray(candidates) ? candidates : [])
|
|
306
|
+
.map((candidate) => ({
|
|
307
|
+
...candidate,
|
|
308
|
+
evaluation: evaluateAutonomyCandidate(candidate, context),
|
|
309
|
+
}))
|
|
310
|
+
.filter((candidate) => candidate.evaluation && candidate.evaluation.admissible)
|
|
311
|
+
.sort(compareEvaluatedCandidates)[0] || null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function buildTaskHistoryKeywords(allTasks, agentName, availableSkills) {
|
|
315
|
+
const keywords = new Set();
|
|
316
|
+
const tasks = Array.isArray(allTasks) ? allTasks : [];
|
|
317
|
+
|
|
318
|
+
for (const task of tasks) {
|
|
319
|
+
if (!task || task.assignee !== agentName || task.status !== 'done') continue;
|
|
320
|
+
const words = `${task.title || ''} ${task.description || ''}`
|
|
321
|
+
.toLowerCase()
|
|
322
|
+
.split(/\W+/)
|
|
323
|
+
.filter((word) => word.length > 3);
|
|
324
|
+
for (const word of words) keywords.add(word);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const skill of Array.isArray(availableSkills) ? availableSkills : []) {
|
|
328
|
+
const token = normalizeText(skill);
|
|
329
|
+
if (!token) continue;
|
|
330
|
+
keywords.add(token.toLowerCase());
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return keywords;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function computeTaskAffinityScore(task, historyKeywords) {
|
|
337
|
+
if (!task || !(historyKeywords instanceof Set) || historyKeywords.size === 0) return 0;
|
|
338
|
+
const words = `${task.title || ''} ${task.description || ''}`
|
|
339
|
+
.toLowerCase()
|
|
340
|
+
.split(/\W+/)
|
|
341
|
+
.filter((word) => word.length > 3);
|
|
342
|
+
return words.filter((word) => historyKeywords.has(word)).length;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function rankClaimableTasks(tasks, context = {}, options = {}) {
|
|
346
|
+
const allTasks = Array.isArray(options.allTasks) ? options.allTasks : tasks;
|
|
347
|
+
const historyKeywords = buildTaskHistoryKeywords(allTasks, context.agent_name, options.availableSkills || context.available_skills);
|
|
348
|
+
|
|
349
|
+
return (Array.isArray(tasks) ? tasks : [])
|
|
350
|
+
.map((task, index) => {
|
|
351
|
+
const target = {
|
|
352
|
+
work_type: 'task',
|
|
353
|
+
title: task.title || '',
|
|
354
|
+
description: task.description || '',
|
|
355
|
+
assigned: false,
|
|
356
|
+
affinity_score: computeTaskAffinityScore(task, historyKeywords),
|
|
357
|
+
required_capabilities: task.required_capabilities || null,
|
|
358
|
+
preferred_capabilities: task.preferred_capabilities || null,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
id: task.id || `task_${index}`,
|
|
363
|
+
order: Number.isFinite(options.orderOffset) ? options.orderOffset + index : index,
|
|
364
|
+
task,
|
|
365
|
+
target,
|
|
366
|
+
evaluation: evaluateAutonomyCandidate({ target }, context),
|
|
367
|
+
};
|
|
368
|
+
})
|
|
369
|
+
.filter((entry) => entry.evaluation.admissible)
|
|
370
|
+
.sort(compareEvaluatedCandidates);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
module.exports = {
|
|
374
|
+
analyzeCapabilityFit,
|
|
375
|
+
compareEvaluatedCandidates,
|
|
376
|
+
evaluateAutonomyCandidate,
|
|
377
|
+
rankClaimableTasks,
|
|
378
|
+
resolveAgentDecisionContext,
|
|
379
|
+
selectAutonomyDecisionCandidate,
|
|
380
|
+
};
|