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.
Files changed (166) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +158 -592
  3. package/SECURITY.md +3 -3
  4. package/USAGE.md +151 -0
  5. package/agent-contracts.js +447 -0
  6. package/api-agents.js +760 -0
  7. package/autonomy/decision-v2.js +380 -0
  8. package/autonomy/watchdog-policy.js +572 -0
  9. package/cli.js +454 -298
  10. package/conversation-templates/autonomous-feature.json +83 -22
  11. package/conversation-templates/code-review.json +69 -21
  12. package/conversation-templates/debug-squad.json +69 -21
  13. package/conversation-templates/feature-build.json +69 -21
  14. package/conversation-templates/research-write.json +69 -21
  15. package/dashboard.html +3148 -174
  16. package/dashboard.js +823 -786
  17. package/data-dir.js +58 -0
  18. package/docs/architecture/branch-semantics.md +157 -0
  19. package/docs/architecture/canonical-event-schema.md +88 -0
  20. package/docs/architecture/markdown-workspace.md +183 -0
  21. package/docs/architecture/runtime-contract.md +459 -0
  22. package/docs/architecture/runtime-migration-hardening.md +64 -0
  23. package/events/hooks.js +154 -0
  24. package/events/log.js +457 -0
  25. package/events/replay.js +33 -0
  26. package/events/schema.js +432 -0
  27. package/managed-team-integration.js +261 -0
  28. package/office/agents.js +704 -597
  29. package/office/animation.js +1 -1
  30. package/office/assets/arcade-cabinet.js +141 -0
  31. package/office/assets/archway.js +77 -0
  32. package/office/assets/bar-counter.js +91 -0
  33. package/office/assets/bar-stool.js +71 -0
  34. package/office/assets/beanbag.js +64 -0
  35. package/office/assets/bench.js +99 -0
  36. package/office/assets/bollard.js +87 -0
  37. package/office/assets/cactus.js +100 -0
  38. package/office/assets/carpet-tile.js +46 -0
  39. package/office/assets/chair.js +123 -0
  40. package/office/assets/chandelier.js +107 -0
  41. package/office/assets/coffee-machine.js +95 -0
  42. package/office/assets/coffee-table.js +81 -0
  43. package/office/assets/column.js +95 -0
  44. package/office/assets/desk-lamp.js +102 -0
  45. package/office/assets/desk.js +76 -0
  46. package/office/assets/dining-table.js +105 -0
  47. package/office/assets/door.js +70 -0
  48. package/office/assets/dual-monitor.js +72 -0
  49. package/office/assets/fence.js +76 -0
  50. package/office/assets/filing-cabinet.js +111 -0
  51. package/office/assets/floor-lamp.js +69 -0
  52. package/office/assets/floor-tile.js +54 -0
  53. package/office/assets/flower-pot.js +76 -0
  54. package/office/assets/foosball.js +95 -0
  55. package/office/assets/fridge.js +99 -0
  56. package/office/assets/gaming-chair.js +154 -0
  57. package/office/assets/gaming-desk.js +105 -0
  58. package/office/assets/glass-door.js +72 -0
  59. package/office/assets/glass-wall.js +64 -0
  60. package/office/assets/half-wall.js +49 -0
  61. package/office/assets/hanging-plant.js +112 -0
  62. package/office/assets/index.js +151 -0
  63. package/office/assets/indoor-tree.js +90 -0
  64. package/office/assets/l-sofa.js +153 -0
  65. package/office/assets/marble-floor.js +64 -0
  66. package/office/assets/materials.js +40 -0
  67. package/office/assets/meeting-table.js +88 -0
  68. package/office/assets/microwave.js +94 -0
  69. package/office/assets/monitor.js +67 -0
  70. package/office/assets/neon-strip.js +73 -0
  71. package/office/assets/painting.js +84 -0
  72. package/office/assets/palm-tree.js +108 -0
  73. package/office/assets/pc-tower.js +91 -0
  74. package/office/assets/pendant-light.js +67 -0
  75. package/office/assets/ping-pong.js +114 -0
  76. package/office/assets/plant.js +72 -0
  77. package/office/assets/planter-box.js +95 -0
  78. package/office/assets/pool-table.js +94 -0
  79. package/office/assets/printer.js +113 -0
  80. package/office/assets/reception-desk.js +133 -0
  81. package/office/assets/rug.js +78 -0
  82. package/office/assets/sculpture.js +85 -0
  83. package/office/assets/server-rack.js +98 -0
  84. package/office/assets/sink.js +109 -0
  85. package/office/assets/sofa.js +106 -0
  86. package/office/assets/speaker.js +83 -0
  87. package/office/assets/spotlight.js +83 -0
  88. package/office/assets/street-lamp.js +97 -0
  89. package/office/assets/trash-can.js +83 -0
  90. package/office/assets/treadmill.js +126 -0
  91. package/office/assets/trophy.js +89 -0
  92. package/office/assets/tv-screen.js +79 -0
  93. package/office/assets/vase.js +84 -0
  94. package/office/assets/wall-clock.js +84 -0
  95. package/office/assets/wall.js +53 -0
  96. package/office/assets/water-cooler.js +146 -0
  97. package/office/assets/whiteboard.js +115 -0
  98. package/office/assets.js +3 -431
  99. package/office/builder.js +791 -355
  100. package/office/campus-env.js +1012 -1119
  101. package/office/environment.js +2 -0
  102. package/office/gallery.js +997 -0
  103. package/office/index.js +165 -61
  104. package/office/navigation.js +173 -152
  105. package/office/player.js +178 -68
  106. package/office/robot-character.js +272 -0
  107. package/office/spectator-camera.js +33 -10
  108. package/office/state.js +2 -0
  109. package/office/world-save.js +35 -4
  110. package/package.json +57 -3
  111. package/providers/comfyui.js +383 -0
  112. package/providers/dalle.js +79 -0
  113. package/providers/gemini.js +181 -0
  114. package/providers/ollama.js +184 -0
  115. package/providers/replicate.js +115 -0
  116. package/providers/zai.js +183 -0
  117. package/runtime-descriptor.js +270 -0
  118. package/scripts/check-agent-contract-advisory.js +132 -0
  119. package/scripts/check-api-agent-parity.js +277 -0
  120. package/scripts/check-autonomy-v2-decision.js +207 -0
  121. package/scripts/check-autonomy-v2-execution.js +588 -0
  122. package/scripts/check-autonomy-v2-watchdog.js +224 -0
  123. package/scripts/check-branch-fork-snapshot.js +337 -0
  124. package/scripts/check-branch-isolation.js +787 -0
  125. package/scripts/check-branch-semantics.js +139 -0
  126. package/scripts/check-dashboard-control-plane.js +1304 -0
  127. package/scripts/check-docs-onboarding.js +490 -0
  128. package/scripts/check-event-schema.js +276 -0
  129. package/scripts/check-evidence-completion.js +239 -0
  130. package/scripts/check-invariants.js +992 -0
  131. package/scripts/check-lifecycle-hooks.js +525 -0
  132. package/scripts/check-managed-team-integration.js +166 -0
  133. package/scripts/check-markdown-workspace-export.js +548 -0
  134. package/scripts/check-markdown-workspace-safety.js +347 -0
  135. package/scripts/check-markdown-workspace.js +136 -0
  136. package/scripts/check-message-replay.js +429 -0
  137. package/scripts/check-migration-hardening.js +300 -0
  138. package/scripts/check-performance-indexing.js +272 -0
  139. package/scripts/check-provider-capabilities.js +316 -0
  140. package/scripts/check-runtime-contract.js +109 -0
  141. package/scripts/check-session-aware-context.js +172 -0
  142. package/scripts/check-session-lifecycle.js +210 -0
  143. package/scripts/export-markdown-workspace.js +84 -0
  144. package/scripts/fixtures/message-replay/clean.jsonl +2 -0
  145. package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
  146. package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
  147. package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
  148. package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
  149. package/scripts/migrate-legacy-to-canonical.js +201 -0
  150. package/scripts/run-verification-suite.js +242 -0
  151. package/scripts/sync-packaged-docs.js +69 -0
  152. package/server.js +9546 -7216
  153. package/state/agents.js +161 -0
  154. package/state/canonical.js +3068 -0
  155. package/state/dashboard-queries.js +441 -0
  156. package/state/evidence.js +56 -0
  157. package/state/io.js +69 -0
  158. package/state/markdown-workspace.js +951 -0
  159. package/state/messages.js +669 -0
  160. package/state/sessions.js +683 -0
  161. package/state/tasks-workflows.js +92 -0
  162. package/templates/debate.json +2 -2
  163. package/templates/managed.json +4 -4
  164. package/templates/pair.json +2 -2
  165. package/templates/review.json +2 -2
  166. 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
+ };