@holoscript/framework 6.0.3 → 6.0.4
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 +1 -2
- package/ROADMAP.md +68 -66
- package/dist/{InvisibleWallet-BB6tFvRA.d.cts → InvisibleWallet-EFiuaLn3.d.cts} +1 -1
- package/dist/{OrchestratorAgent-BvWgf9uw.d.cts → OrchestratorAgent-CrLDGNL6.d.cts} +1 -1
- package/dist/agents/index.cjs +11 -10
- package/dist/agents/index.d.cts +4 -16
- package/dist/ai/index.cjs +2 -2
- package/dist/behavior.cjs +10 -0
- package/dist/economy/index.cjs +4 -4
- package/dist/economy/index.d.cts +2 -2
- package/dist/index.cjs +33 -11
- package/dist/index.d.cts +3 -3
- package/dist/swarm/index.cjs +3 -0
- package/package.json +14 -9
- package/src/__tests__/bounty-marketplace.test.ts +53 -21
- package/src/__tests__/delegation.test.ts +1 -4
- package/src/__tests__/done-log-audit.test.ts +38 -46
- package/src/__tests__/framework.test.ts +172 -53
- package/src/__tests__/goal-synthesizer.test.ts +9 -6
- package/src/__tests__/presence.test.ts +1 -1
- package/src/__tests__/protocol-agent.test.ts +12 -11
- package/src/__tests__/revenue-splitter.test.ts +22 -15
- package/src/__tests__/scenario-driven-todo.test.ts +55 -35
- package/src/__tests__/self-improve.test.ts +28 -9
- package/src/__tests__/service-lifecycle.test.ts +9 -3
- package/src/__tests__/skill-router.test.ts +3 -3
- package/src/agents/CulturalMemory.ts +6 -6
- package/src/agents/DelegationTraceHooks.ts +560 -0
- package/src/agents/FederatedRegistryAdapter.ts +1 -1
- package/src/agents/NormEngine.ts +3 -8
- package/src/agents/OrchestratorAgent.ts +1 -1
- package/src/agents/TaskDelegationService.ts +5 -9
- package/src/agents/__tests__/AgentWalletRegistry.test.ts +5 -4
- package/src/agents/__tests__/CrossRealityHandoff.test.ts +9 -3
- package/src/agents/__tests__/DelegationTraceHooks.test.ts +390 -0
- package/src/agents/__tests__/TaskDelegationService.test.ts +4 -2
- package/src/agents/spatial-comms/Layer1RealTime.ts +36 -19
- package/src/agents/spatial-comms/Layer2A2A.ts +1 -3
- package/src/agents/spatial-comms/Layer3MCP.ts +13 -4
- package/src/agents/spatial-comms/ProtocolTypes.ts +5 -2
- package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +2 -2
- package/src/ai/HoloScriptGenerator.ts +2 -2
- package/src/ai/__tests__/PerceptionSystem.prod.test.ts +1 -1
- package/src/ai/__tests__/PerceptionSystem.test.ts +14 -14
- package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +1 -1
- package/src/ai/index.ts +5 -1
- package/src/board/audit.ts +17 -6
- package/src/board/board-ops.ts +45 -15
- package/src/board/board-types.ts +94 -20
- package/src/delegation.ts +5 -3
- package/src/distributed-claimer.ts +13 -2
- package/src/economy/BountyManager.ts +40 -18
- package/src/economy/KnowledgeMarketplace.ts +27 -8
- package/src/economy/PaymentWebhookService.ts +0 -1
- package/src/economy/RevenueSplitter.ts +2 -4
- package/src/economy/UnifiedBudgetOptimizer.ts +8 -9
- package/src/economy/_core-stubs.ts +1 -1
- package/src/economy/x402-facilitator.ts +17 -8
- package/src/index.ts +16 -12
- package/src/knowledge/__tests__/knowledge-consolidator.test.ts +138 -89
- package/src/knowledge/__tests__/knowledge-store-vector.test.ts +59 -16
- package/src/knowledge/brain.ts +7 -7
- package/src/knowledge/consolidation.ts +16 -16
- package/src/knowledge/knowledge-consolidator.ts +60 -30
- package/src/knowledge/knowledge-store.ts +83 -45
- package/src/learning/ProceduralCompiler.ts +6 -1
- package/src/learning/learning/MemoryConsolidator.ts +102 -0
- package/src/learning/learning/MemoryScorer.ts +69 -0
- package/src/learning/learning/ProceduralCompiler.ts +45 -0
- package/src/learning/learning/SemanticClusterer.ts +66 -0
- package/src/llm/llm-adapter.ts +24 -10
- package/src/mesh/index.ts +37 -17
- package/src/protocol/goal-synthesizer.ts +24 -34
- package/src/protocol/implementations.ts +91 -22
- package/src/protocol/micro-phase-decomposer.ts +25 -17
- package/src/protocol/micro-step-decomposer.test.ts +104 -39
- package/src/protocol-agent.test.ts +17 -7
- package/src/protocol-agent.ts +45 -42
- package/src/self-improve/absorb-scanner.ts +9 -6
- package/src/self-improve/evolution-engine.ts +36 -18
- package/src/self-improve/framework-absorber.ts +21 -16
- package/src/self-improve/index.ts +2 -10
- package/src/self-improve/prompt-optimizer.ts +31 -19
- package/src/self-improve/test-generator.ts +16 -12
- package/src/skill-router.ts +7 -6
- package/src/swarm/messaging/GossipProtocol.ts +1 -1
- package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +31 -9
- package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +21 -7
- package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +24 -8
- package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +6 -2
- package/src/team.ts +277 -122
- package/src/training/scripts/generate-spatial-dataset.ts +1 -1
- package/src/training/training/LRScheduler.ts +377 -0
- package/src/training/training/QualityScoringPipeline.ts +139 -0
- package/src/training/training/SoftDedup.ts +461 -0
- package/src/training/training/SparsityMonitor.ts +685 -0
- package/src/training/training/SparsityMonitorTypes.ts +209 -0
- package/src/training/training/SpatialTrainingDataGenerator.ts +1526 -0
- package/src/training/training/SpatialTrainingDataTypes.ts +216 -0
- package/src/training/training/TrainingPipelineConfig.ts +215 -0
- package/src/training/training/__tests__/CorpusValidation.test.ts +87 -0
- package/src/training/training/__tests__/LRScheduler.test.ts +592 -0
- package/src/training/training/__tests__/SoftDedup.test.ts +415 -0
- package/src/training/training/__tests__/SparsityMonitor.test.ts +1623 -0
- package/src/training/training/__tests__/SpatialCorpusValidation.test.ts +72 -0
- package/src/training/training/__tests__/SpatialTrainingDataGenerator.test.ts +1244 -0
- package/src/training/training/__tests__/TrainingMonkeyIntegration.test.ts +897 -0
- package/src/training/training/__tests__/TrainingPipelineConfig.test.ts +202 -0
- package/src/training/training/__tests__/schema.test.ts +72 -0
- package/src/training/training/__tests__/training-constants.test.ts +106 -0
- package/src/training/training/__tests__/trait-mappings.test.ts +81 -0
- package/src/training/training/constants.ts +94 -0
- package/src/training/training/index.ts +17 -0
- package/src/training/training/schema.ts +147 -0
- package/src/training/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
- package/src/training/training/scripts/generate-spatial-dataset.ts +521 -0
- package/src/training/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
- package/src/training/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
- package/src/training/training/trainingmonkey/index.ts +26 -0
- package/src/training/training/trait-mappings.ts +157 -0
- package/src/types.ts +2 -7
- package/ALL-test-results.json +0 -1
- package/LICENSE +0 -21
- package/dist/AgentManifest-CB4xM-Ma.d.ts +0 -704
- package/dist/BehaviorTree-BrBFECv5.d.ts +0 -103
- package/dist/InvisibleWallet-rtRrBOA8.d.ts +0 -1732
- package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +0 -798
- package/dist/agents/index.d.ts +0 -1788
- package/dist/agents/index.js +0 -4695
- package/dist/ai/index.d.ts +0 -1753
- package/dist/ai/index.js +0 -5244
- package/dist/behavior.d.ts +0 -130
- package/dist/behavior.js +0 -407
- package/dist/economy/index.d.ts +0 -747
- package/dist/economy/index.js +0 -3617
- package/dist/implementations-D9T3un9D.d.ts +0 -236
- package/dist/index.d.ts +0 -1729
- package/dist/index.js +0 -24277
- package/dist/learning/index.d.ts +0 -104
- package/dist/learning/index.js +0 -189
- package/dist/negotiation/index.d.ts +0 -610
- package/dist/negotiation/index.js +0 -931
- package/dist/skills/index.d.ts +0 -289
- package/dist/skills/index.js +0 -1079
- package/dist/swarm/index.d.ts +0 -2433
- package/dist/swarm/index.js +0 -5221
- package/dist/training/index.d.ts +0 -1734
- package/dist/training/index.js +0 -2687
- package/extract-failures.js +0 -10
- package/src/training/training/data/novel-use-cases.jsonl +0 -153
- package/src/training/training/data/spatial-reasoning-10k.jsonl +0 -9354
- package/src/types/core-stubs.d.ts +0 -113
- package/test-output.txt +0 -0
- package/test-result.json +0 -1
- package/tsc-errors.txt +0 -4
- package/tsc_output.txt +0 -0
- package/typescript-errors-2.txt +0 -0
- package/typescript-errors.txt +0 -22
- package/vitest-log-utf8.txt +0 -268
- package/vitest-log.txt +0 -0
package/src/team.ts
CHANGED
|
@@ -50,13 +50,19 @@ import { parseDeriveContent, ROOM_PRESETS } from './board';
|
|
|
50
50
|
import { MeshDiscovery, SignalService, GossipProtocol } from './mesh';
|
|
51
51
|
import type { PeerMetadata, GossipPacket } from './mesh';
|
|
52
52
|
import { BountyManager } from './economy/BountyManager';
|
|
53
|
-
import type {
|
|
53
|
+
import type {
|
|
54
|
+
Bounty,
|
|
55
|
+
BountyReward,
|
|
56
|
+
ClaimResult as BountyClaimResult,
|
|
57
|
+
CompletionProof,
|
|
58
|
+
PayoutResult,
|
|
59
|
+
} from './economy/BountyManager';
|
|
54
60
|
|
|
55
61
|
// ── Mode Claim Filters (FW-0.3) ──
|
|
56
62
|
// Each mode defines which SlotRoles can actively claim tasks.
|
|
57
63
|
// Agents whose claimFilter.roles don't overlap with the active set are deprioritized.
|
|
58
64
|
|
|
59
|
-
const
|
|
65
|
+
const _MODE_CLAIM_ROLES: Record<TeamMode, SlotRole[]> = {
|
|
60
66
|
audit: ['researcher', 'reviewer', 'tester'],
|
|
61
67
|
build: ['coder', 'tester'],
|
|
62
68
|
research: ['researcher'],
|
|
@@ -96,7 +102,7 @@ function computeTier(score: number): ReputationTier {
|
|
|
96
102
|
const OPEN_TASK_THRESHOLD = 3;
|
|
97
103
|
|
|
98
104
|
/** Default presence timeouts (FW-0.3). */
|
|
99
|
-
const DEFAULT_IDLE_TIMEOUT_MS = 60_000;
|
|
105
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 60_000; // 60 seconds
|
|
100
106
|
const DEFAULT_OFFLINE_TIMEOUT_MS = 300_000; // 5 minutes
|
|
101
107
|
|
|
102
108
|
/** Internal record for tracking an agent's presence state. */
|
|
@@ -114,7 +120,12 @@ export class Team {
|
|
|
114
120
|
private agentConfigs: AgentConfig[];
|
|
115
121
|
private runtimes: Map<string, AgentRuntime> = new Map();
|
|
116
122
|
private board: TaskDef[] = [];
|
|
117
|
-
private doneLog: Array<{
|
|
123
|
+
private doneLog: Array<{
|
|
124
|
+
taskId: string;
|
|
125
|
+
title: string;
|
|
126
|
+
completedBy: string;
|
|
127
|
+
timestamp: string;
|
|
128
|
+
}> = [];
|
|
118
129
|
private cycle = 0;
|
|
119
130
|
private consensusMode: ConsensusMode;
|
|
120
131
|
private proposals: Map<string, InternalProposal> = new Map();
|
|
@@ -146,9 +157,7 @@ export class Team {
|
|
|
146
157
|
this.idleTimeoutMs = config.presence?.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
147
158
|
this.offlineTimeoutMs = config.presence?.offlineTimeoutMs ?? DEFAULT_OFFLINE_TIMEOUT_MS;
|
|
148
159
|
|
|
149
|
-
this.knowledge = new KnowledgeStore(
|
|
150
|
-
config.knowledge ?? { persist: false }
|
|
151
|
-
);
|
|
160
|
+
this.knowledge = new KnowledgeStore(config.knowledge ?? { persist: false });
|
|
152
161
|
|
|
153
162
|
// GoalSynthesizer gets knowledge store so it can derive context-aware goals (FW-0.2)
|
|
154
163
|
// LLM config comes from first agent — all agents on a team typically share a provider.
|
|
@@ -218,15 +227,24 @@ export class Team {
|
|
|
218
227
|
/** Add tasks to the board. Deduplicates by normalized title. */
|
|
219
228
|
async addTasks(tasks: Array<Omit<TaskDef, 'id' | 'status' | 'createdAt'>>): Promise<TaskDef[]> {
|
|
220
229
|
if (this.isRemote) {
|
|
221
|
-
const res = await this.boardFetch(
|
|
230
|
+
const res = await this.boardFetch(
|
|
231
|
+
`/api/holomesh/team/${encodeURIComponent(this.name)}/board`,
|
|
232
|
+
'POST',
|
|
233
|
+
{ tasks }
|
|
234
|
+
);
|
|
222
235
|
if (res && res.error) throw new Error(String(res.error));
|
|
223
236
|
return (res?.tasks || res?.added || []) as TaskDef[];
|
|
224
237
|
}
|
|
225
238
|
|
|
226
|
-
const normalize = (s: string) =>
|
|
239
|
+
const normalize = (s: string) =>
|
|
240
|
+
s
|
|
241
|
+
.toLowerCase()
|
|
242
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
243
|
+
.trim()
|
|
244
|
+
.slice(0, 60);
|
|
227
245
|
const existing = new Set([
|
|
228
|
-
...this.board.map(t => normalize(t.title)),
|
|
229
|
-
...this.doneLog.map(d => normalize(d.title)),
|
|
246
|
+
...this.board.map((t) => normalize(t.title)),
|
|
247
|
+
...this.doneLog.map((d) => normalize(d.title)),
|
|
230
248
|
]);
|
|
231
249
|
|
|
232
250
|
const added: TaskDef[] = [];
|
|
@@ -253,9 +271,7 @@ export class Team {
|
|
|
253
271
|
|
|
254
272
|
/** Get all open tasks, sorted by priority. */
|
|
255
273
|
get openTasks(): TaskDef[] {
|
|
256
|
-
return this.board
|
|
257
|
-
.filter(t => t.status === 'open')
|
|
258
|
-
.sort((a, b) => a.priority - b.priority);
|
|
274
|
+
return this.board.filter((t) => t.status === 'open').sort((a, b) => a.priority - b.priority);
|
|
259
275
|
}
|
|
260
276
|
|
|
261
277
|
/** Get all board tasks. */
|
|
@@ -302,7 +318,7 @@ export class Team {
|
|
|
302
318
|
teamName: this.name,
|
|
303
319
|
agentName: runtime.name,
|
|
304
320
|
capabilities: agent.capabilities,
|
|
305
|
-
recentCompletedTasks: this.doneLog.slice(-10).map(d => d.title),
|
|
321
|
+
recentCompletedTasks: this.doneLog.slice(-10).map((d) => d.title),
|
|
306
322
|
};
|
|
307
323
|
try {
|
|
308
324
|
const goals = await this.goalSynthesizer.synthesizeMultiple(goalContext, 1);
|
|
@@ -344,7 +360,7 @@ export class Team {
|
|
|
344
360
|
// Mark done
|
|
345
361
|
task.status = 'done';
|
|
346
362
|
task.completedAt = new Date().toISOString();
|
|
347
|
-
this.board = this.board.filter(t => t.id !== task.id);
|
|
363
|
+
this.board = this.board.filter((t) => t.id !== task.id);
|
|
348
364
|
this.doneLog.push({
|
|
349
365
|
taskId: task.id,
|
|
350
366
|
title: task.title,
|
|
@@ -407,8 +423,9 @@ export class Team {
|
|
|
407
423
|
|
|
408
424
|
// Fetch board state from remote
|
|
409
425
|
const boardData = await this.boardFetch(`/api/holomesh/team/${teamId}/board`, 'GET');
|
|
410
|
-
const openTasks = (
|
|
411
|
-
|
|
426
|
+
const openTasks = (
|
|
427
|
+
((boardData?.board as Record<string, unknown>)?.open as TaskDef[]) || []
|
|
428
|
+
).sort((a, b) => a.priority - b.priority);
|
|
412
429
|
|
|
413
430
|
const claimed = new Set<string>();
|
|
414
431
|
|
|
@@ -424,25 +441,31 @@ export class Team {
|
|
|
424
441
|
const domain = agent.knowledgeDomains?.[0] ?? 'general';
|
|
425
442
|
let goal: Goal;
|
|
426
443
|
try {
|
|
427
|
-
const goals = await this.goalSynthesizer.synthesizeMultiple(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
444
|
+
const goals = await this.goalSynthesizer.synthesizeMultiple(
|
|
445
|
+
{
|
|
446
|
+
domain,
|
|
447
|
+
teamName: this.name,
|
|
448
|
+
agentName: runtime.name,
|
|
449
|
+
capabilities: agent.capabilities,
|
|
450
|
+
recentCompletedTasks: this.doneLog.slice(-10).map((d) => d.title),
|
|
451
|
+
},
|
|
452
|
+
1
|
|
453
|
+
);
|
|
434
454
|
goal = goals[0] ?? this.goalSynthesizer.synthesize(domain, 'autonomous-boredom');
|
|
435
455
|
} catch {
|
|
436
456
|
goal = this.goalSynthesizer.synthesize(domain, 'autonomous-boredom');
|
|
437
457
|
}
|
|
438
458
|
const addRes = await this.boardFetch(`/api/holomesh/team/${teamId}/board`, 'POST', {
|
|
439
|
-
tasks: [
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
459
|
+
tasks: [
|
|
460
|
+
{
|
|
461
|
+
title: goal.description,
|
|
462
|
+
description: `Autonomously synthesized goal [${goal.category}] — priority ${goal.priority}`,
|
|
463
|
+
priority: goal.priority === 'high' ? 2 : goal.priority === 'medium' ? 4 : 6,
|
|
464
|
+
role:
|
|
465
|
+
agent.role === 'architect' || agent.role === 'researcher' ? 'researcher' : 'coder',
|
|
466
|
+
source: `synthesizer:${goal.source}`,
|
|
467
|
+
},
|
|
468
|
+
],
|
|
446
469
|
});
|
|
447
470
|
const addedTasks = (addRes?.tasks || addRes?.added || []) as TaskDef[];
|
|
448
471
|
task = addedTasks[0];
|
|
@@ -450,7 +473,14 @@ export class Team {
|
|
|
450
473
|
}
|
|
451
474
|
|
|
452
475
|
if (!task) {
|
|
453
|
-
results.push({
|
|
476
|
+
results.push({
|
|
477
|
+
agentName: runtime.name,
|
|
478
|
+
taskId: null,
|
|
479
|
+
taskTitle: null,
|
|
480
|
+
action: 'skipped',
|
|
481
|
+
summary: 'No matching open tasks',
|
|
482
|
+
knowledge: [],
|
|
483
|
+
});
|
|
454
484
|
continue;
|
|
455
485
|
}
|
|
456
486
|
|
|
@@ -460,9 +490,20 @@ export class Team {
|
|
|
460
490
|
this.localHeartbeat(runtime.name, task.title);
|
|
461
491
|
|
|
462
492
|
// Claim via API
|
|
463
|
-
const claimRes = await this.boardFetch(
|
|
493
|
+
const claimRes = await this.boardFetch(
|
|
494
|
+
`/api/holomesh/team/${teamId}/board/${encodeURIComponent(task.id)}`,
|
|
495
|
+
'PATCH',
|
|
496
|
+
{ action: 'claim' }
|
|
497
|
+
);
|
|
464
498
|
if (claimRes?.error) {
|
|
465
|
-
results.push({
|
|
499
|
+
results.push({
|
|
500
|
+
agentName: runtime.name,
|
|
501
|
+
taskId: task.id,
|
|
502
|
+
taskTitle: task.title,
|
|
503
|
+
action: 'error',
|
|
504
|
+
summary: `Claim failed: ${claimRes.error}`,
|
|
505
|
+
knowledge: [],
|
|
506
|
+
});
|
|
466
507
|
continue;
|
|
467
508
|
}
|
|
468
509
|
|
|
@@ -477,7 +518,11 @@ export class Team {
|
|
|
477
518
|
// Proactively buy relevant knowledge if cheap enough (e.g. < 5 credits)
|
|
478
519
|
const activeListings = this.knowledge.marketplace.activeListings();
|
|
479
520
|
for (const listing of activeListings) {
|
|
480
|
-
if (
|
|
521
|
+
if (
|
|
522
|
+
listing.price <= 5 &&
|
|
523
|
+
listing.currency === 'credits' &&
|
|
524
|
+
task.title.toLowerCase().includes(listing.preview.domain?.toLowerCase() || '')
|
|
525
|
+
) {
|
|
481
526
|
this.knowledge.marketplace.buyKnowledge(listing.id, runtime.name);
|
|
482
527
|
}
|
|
483
528
|
}
|
|
@@ -487,7 +532,11 @@ export class Team {
|
|
|
487
532
|
const { summary, insights } = await this.executeTask(runtime, task);
|
|
488
533
|
|
|
489
534
|
// Mark done via API
|
|
490
|
-
await this.boardFetch(
|
|
535
|
+
await this.boardFetch(
|
|
536
|
+
`/api/holomesh/team/${teamId}/board/${encodeURIComponent(task.id)}`,
|
|
537
|
+
'PATCH',
|
|
538
|
+
{ action: 'done', summary }
|
|
539
|
+
);
|
|
491
540
|
|
|
492
541
|
// Complete the bounty with proof
|
|
493
542
|
for (const b of taskBounties) {
|
|
@@ -507,11 +556,29 @@ export class Team {
|
|
|
507
556
|
runtime.reputationTier = computeTier(runtime.reputationScore);
|
|
508
557
|
|
|
509
558
|
allInsights.push(...insights);
|
|
510
|
-
results.push({
|
|
559
|
+
results.push({
|
|
560
|
+
agentName: runtime.name,
|
|
561
|
+
taskId: task.id,
|
|
562
|
+
taskTitle: task.title,
|
|
563
|
+
action: synthesized ? 'synthesized' : 'completed',
|
|
564
|
+
summary,
|
|
565
|
+
knowledge: insights,
|
|
566
|
+
});
|
|
511
567
|
} catch (err) {
|
|
512
568
|
// Reopen task on failure
|
|
513
|
-
await this.boardFetch(
|
|
514
|
-
|
|
569
|
+
await this.boardFetch(
|
|
570
|
+
`/api/holomesh/team/${teamId}/board/${encodeURIComponent(task.id)}`,
|
|
571
|
+
'PATCH',
|
|
572
|
+
{ action: 'reopen' }
|
|
573
|
+
);
|
|
574
|
+
results.push({
|
|
575
|
+
agentName: runtime.name,
|
|
576
|
+
taskId: task.id,
|
|
577
|
+
taskTitle: task.title,
|
|
578
|
+
action: 'error',
|
|
579
|
+
summary: err instanceof Error ? err.message : String(err),
|
|
580
|
+
knowledge: [],
|
|
581
|
+
});
|
|
515
582
|
}
|
|
516
583
|
}
|
|
517
584
|
|
|
@@ -522,7 +589,14 @@ export class Team {
|
|
|
522
589
|
await this.knowledge.syncToRemote();
|
|
523
590
|
}
|
|
524
591
|
|
|
525
|
-
return {
|
|
592
|
+
return {
|
|
593
|
+
teamName: this.name,
|
|
594
|
+
cycle: this.cycle,
|
|
595
|
+
agentResults: results,
|
|
596
|
+
knowledgeProduced: allInsights,
|
|
597
|
+
compoundedInsights: compounded,
|
|
598
|
+
durationMs: Date.now() - start,
|
|
599
|
+
};
|
|
526
600
|
}
|
|
527
601
|
|
|
528
602
|
// ── Consensus ──
|
|
@@ -549,7 +623,7 @@ export class Team {
|
|
|
549
623
|
}
|
|
550
624
|
}
|
|
551
625
|
|
|
552
|
-
const votesFor = Array.from(proposal.votes.values()).filter(v => v).length;
|
|
626
|
+
const votesFor = Array.from(proposal.votes.values()).filter((v) => v).length;
|
|
553
627
|
const votesAgainst = proposal.votes.size - votesFor;
|
|
554
628
|
const total = proposal.votes.size;
|
|
555
629
|
|
|
@@ -585,8 +659,7 @@ export class Team {
|
|
|
585
659
|
|
|
586
660
|
/** Get the reputation leaderboard. */
|
|
587
661
|
leaderboard(): AgentRuntime[] {
|
|
588
|
-
return Array.from(this.runtimes.values())
|
|
589
|
-
.sort((a, b) => b.reputationScore - a.reputationScore);
|
|
662
|
+
return Array.from(this.runtimes.values()).sort((a, b) => b.reputationScore - a.reputationScore);
|
|
590
663
|
}
|
|
591
664
|
|
|
592
665
|
/** Get a specific agent's runtime. */
|
|
@@ -615,7 +688,7 @@ export class Team {
|
|
|
615
688
|
taskId: String(d.taskId ?? d.task_id ?? ''),
|
|
616
689
|
title: String(d.title ?? ''),
|
|
617
690
|
completedBy: String(d.completedBy ?? d.completed_by ?? ''),
|
|
618
|
-
commitHash: d.commitHash as string | undefined ?? d.commit_hash as string | undefined,
|
|
691
|
+
commitHash: (d.commitHash as string | undefined) ?? (d.commit_hash as string | undefined),
|
|
619
692
|
timestamp: String(d.timestamp ?? d.completedAt ?? ''),
|
|
620
693
|
summary: String(d.summary ?? ''),
|
|
621
694
|
}));
|
|
@@ -666,9 +739,13 @@ export class Team {
|
|
|
666
739
|
const trimmedTitle = title.trim().slice(0, 200);
|
|
667
740
|
if (!trimmedTitle) throw new Error('title is required');
|
|
668
741
|
|
|
669
|
-
const normalize = (s: string) =>
|
|
742
|
+
const normalize = (s: string) =>
|
|
743
|
+
s
|
|
744
|
+
.toLowerCase()
|
|
745
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
746
|
+
.trim();
|
|
670
747
|
const existingNorm = new Set(
|
|
671
|
-
this.localSuggestions.filter(s => s.status === 'open').map(s => normalize(s.title))
|
|
748
|
+
this.localSuggestions.filter((s) => s.status === 'open').map((s) => normalize(s.title))
|
|
672
749
|
);
|
|
673
750
|
if (existingNorm.has(normalize(trimmedTitle))) {
|
|
674
751
|
throw new Error('A similar open suggestion already exists');
|
|
@@ -697,10 +774,17 @@ export class Team {
|
|
|
697
774
|
* Local mode: vote(id, agentName, 'up'|'down', reason?).
|
|
698
775
|
* Remote mode: vote(id, 1|-1, reason?) for backward compatibility.
|
|
699
776
|
*/
|
|
700
|
-
async vote(
|
|
777
|
+
async vote(
|
|
778
|
+
suggestionId: string,
|
|
779
|
+
agentNameOrValue: string | 1 | -1,
|
|
780
|
+
voteOrReason?: 'up' | 'down' | string,
|
|
781
|
+
reason?: string
|
|
782
|
+
): Promise<SuggestionVoteResult> {
|
|
701
783
|
if (this.isRemote) {
|
|
702
|
-
const value =
|
|
703
|
-
|
|
784
|
+
const value =
|
|
785
|
+
typeof agentNameOrValue === 'number' ? agentNameOrValue : voteOrReason === 'down' ? -1 : 1;
|
|
786
|
+
const remoteReason =
|
|
787
|
+
typeof agentNameOrValue === 'number' ? (voteOrReason as string | undefined) : reason;
|
|
704
788
|
const teamId = encodeURIComponent(this.name);
|
|
705
789
|
const res = await this.boardFetch(
|
|
706
790
|
`/api/holomesh/team/${teamId}/suggestions/${encodeURIComponent(suggestionId)}`,
|
|
@@ -713,38 +797,51 @@ export class Team {
|
|
|
713
797
|
}
|
|
714
798
|
|
|
715
799
|
const agentName = String(agentNameOrValue);
|
|
716
|
-
const voteDir: 'up' | 'down' =
|
|
800
|
+
const voteDir: 'up' | 'down' =
|
|
801
|
+
voteOrReason === 'up' || voteOrReason === 'down' ? voteOrReason : 'up';
|
|
717
802
|
|
|
718
|
-
const suggestion = this.localSuggestions.find(s => s.id === suggestionId);
|
|
803
|
+
const suggestion = this.localSuggestions.find((s) => s.id === suggestionId);
|
|
719
804
|
if (!suggestion) throw new Error('Suggestion not found');
|
|
720
|
-
if (suggestion.status !== 'open')
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
suggestion.votes.
|
|
805
|
+
if (suggestion.status !== 'open')
|
|
806
|
+
throw new Error(`Suggestion is ${suggestion.status}, voting closed`);
|
|
807
|
+
|
|
808
|
+
suggestion.votes = suggestion.votes.filter((v) => v.agent !== agentName);
|
|
809
|
+
suggestion.votes.push({
|
|
810
|
+
agent: agentName,
|
|
811
|
+
vote: voteDir,
|
|
812
|
+
reason,
|
|
813
|
+
votedAt: new Date().toISOString(),
|
|
814
|
+
});
|
|
724
815
|
|
|
725
|
-
const upvotes = suggestion.votes.filter(v => v.vote === 'up').length;
|
|
726
|
-
const downvotes = suggestion.votes.filter(v => v.vote === 'down').length;
|
|
816
|
+
const upvotes = suggestion.votes.filter((v) => v.vote === 'up').length;
|
|
817
|
+
const downvotes = suggestion.votes.filter((v) => v.vote === 'down').length;
|
|
727
818
|
suggestion.score = upvotes - downvotes;
|
|
728
819
|
|
|
729
820
|
let promotedTaskId: string | undefined;
|
|
730
821
|
|
|
731
|
-
const promoteThreshold =
|
|
822
|
+
const promoteThreshold =
|
|
823
|
+
suggestion.autoPromoteThreshold ?? Math.ceil(this.agentConfigs.length / 2);
|
|
732
824
|
if (upvotes >= promoteThreshold && suggestion.status === 'open') {
|
|
733
825
|
suggestion.status = 'promoted';
|
|
734
826
|
suggestion.resolvedAt = new Date().toISOString();
|
|
735
|
-
const promoted = await this.addTasks([
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
827
|
+
const promoted = await this.addTasks([
|
|
828
|
+
{
|
|
829
|
+
title: suggestion.title,
|
|
830
|
+
description:
|
|
831
|
+
`${suggestion.description ?? ''}\n\n[Auto-promoted from suggestion by ${suggestion.proposedBy} with ${suggestion.score} net votes]`.trim(),
|
|
832
|
+
priority:
|
|
833
|
+
suggestion.category === 'architecture' ? 2 : suggestion.category === 'testing' ? 3 : 4,
|
|
834
|
+
source: `suggestion:${suggestion.id}`,
|
|
835
|
+
},
|
|
836
|
+
]);
|
|
741
837
|
if (promoted.length > 0) {
|
|
742
838
|
promotedTaskId = promoted[0].id;
|
|
743
839
|
suggestion.promotedTaskId = promotedTaskId;
|
|
744
840
|
}
|
|
745
841
|
}
|
|
746
842
|
|
|
747
|
-
const dismissThreshold =
|
|
843
|
+
const dismissThreshold =
|
|
844
|
+
suggestion.autoDismissThreshold ?? Math.ceil(this.agentConfigs.length / 2);
|
|
748
845
|
if (downvotes >= dismissThreshold && suggestion.status === 'open') {
|
|
749
846
|
suggestion.status = 'dismissed';
|
|
750
847
|
suggestion.resolvedAt = new Date().toISOString();
|
|
@@ -768,28 +865,36 @@ export class Team {
|
|
|
768
865
|
}
|
|
769
866
|
|
|
770
867
|
const filtered = status
|
|
771
|
-
? this.localSuggestions.filter(s => s.status === status)
|
|
868
|
+
? this.localSuggestions.filter((s) => s.status === status)
|
|
772
869
|
: [...this.localSuggestions];
|
|
773
870
|
return { suggestions: filtered };
|
|
774
871
|
}
|
|
775
872
|
|
|
776
873
|
/** Promote a suggestion to a board task manually. Local-only. */
|
|
777
|
-
async promoteSuggestion(
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
874
|
+
async promoteSuggestion(
|
|
875
|
+
suggestionId: string,
|
|
876
|
+
promoterName?: string
|
|
877
|
+
): Promise<SuggestionVoteResult> {
|
|
878
|
+
if (this.isRemote)
|
|
879
|
+
throw new Error('promoteSuggestion() is not supported in remote mode — use the board API');
|
|
880
|
+
|
|
881
|
+
const suggestion = this.localSuggestions.find((s) => s.id === suggestionId);
|
|
781
882
|
if (!suggestion) throw new Error('Suggestion not found');
|
|
782
883
|
if (suggestion.status !== 'open') throw new Error(`Suggestion is already ${suggestion.status}`);
|
|
783
884
|
|
|
784
885
|
suggestion.status = 'promoted';
|
|
785
886
|
suggestion.resolvedAt = new Date().toISOString();
|
|
786
887
|
|
|
787
|
-
const promoted = await this.addTasks([
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
888
|
+
const promoted = await this.addTasks([
|
|
889
|
+
{
|
|
890
|
+
title: suggestion.title,
|
|
891
|
+
description:
|
|
892
|
+
`${suggestion.description ?? ''}\n\n[Promoted by ${promoterName ?? 'team'} from suggestion by ${suggestion.proposedBy}]`.trim(),
|
|
893
|
+
priority:
|
|
894
|
+
suggestion.category === 'architecture' ? 2 : suggestion.category === 'testing' ? 3 : 4,
|
|
895
|
+
source: `suggestion:${suggestion.id}`,
|
|
896
|
+
},
|
|
897
|
+
]);
|
|
793
898
|
|
|
794
899
|
const promotedTaskId = promoted.length > 0 ? promoted[0].id : undefined;
|
|
795
900
|
suggestion.promotedTaskId = promotedTaskId;
|
|
@@ -798,9 +903,10 @@ export class Team {
|
|
|
798
903
|
|
|
799
904
|
/** Dismiss a suggestion. Local-only. */
|
|
800
905
|
dismissSuggestion(suggestionId: string): SuggestionVoteResult {
|
|
801
|
-
if (this.isRemote)
|
|
906
|
+
if (this.isRemote)
|
|
907
|
+
throw new Error('dismissSuggestion() is not supported in remote mode — use the board API');
|
|
802
908
|
|
|
803
|
-
const suggestion = this.localSuggestions.find(s => s.id === suggestionId);
|
|
909
|
+
const suggestion = this.localSuggestions.find((s) => s.id === suggestionId);
|
|
804
910
|
if (!suggestion) throw new Error('Suggestion not found');
|
|
805
911
|
if (suggestion.status !== 'open') throw new Error(`Suggestion is already ${suggestion.status}`);
|
|
806
912
|
|
|
@@ -829,7 +935,8 @@ export class Team {
|
|
|
829
935
|
/** Switch the team's operating mode. Local-first with optional remote sync. */
|
|
830
936
|
async setMode(mode: TeamMode): Promise<SetModeResult> {
|
|
831
937
|
const preset = ROOM_PRESETS[mode];
|
|
832
|
-
if (!preset)
|
|
938
|
+
if (!preset)
|
|
939
|
+
throw new Error(`Unknown mode: ${mode}. Valid: ${Object.keys(ROOM_PRESETS).join(', ')}`);
|
|
833
940
|
|
|
834
941
|
const previousMode = this.currentMode;
|
|
835
942
|
this.currentMode = mode;
|
|
@@ -857,7 +964,10 @@ export class Team {
|
|
|
857
964
|
async derive(source: string, content: string): Promise<DeriveResult> {
|
|
858
965
|
if (this.isRemote) {
|
|
859
966
|
const teamId = encodeURIComponent(this.name);
|
|
860
|
-
const res = await this.boardFetch(`/api/holomesh/team/${teamId}/board/derive`, 'POST', {
|
|
967
|
+
const res = await this.boardFetch(`/api/holomesh/team/${teamId}/board/derive`, 'POST', {
|
|
968
|
+
source,
|
|
969
|
+
content,
|
|
970
|
+
});
|
|
861
971
|
if (!res) throw new Error('Failed to derive tasks — no response from board');
|
|
862
972
|
if (res.error) throw new Error(String(res.error));
|
|
863
973
|
return res as unknown as DeriveResult;
|
|
@@ -879,13 +989,18 @@ export class Team {
|
|
|
879
989
|
* Modifies both boards. Requires connected remote board.
|
|
880
990
|
*/
|
|
881
991
|
async delegate(otherTeamId: string, taskId: string): Promise<boolean> {
|
|
882
|
-
if (!this.isRemote)
|
|
992
|
+
if (!this.isRemote)
|
|
993
|
+
throw new Error('Delegation requires a remote board to communicate with other teams');
|
|
883
994
|
const teamId = encodeURIComponent(this.name);
|
|
884
|
-
const res = await this.boardFetch(
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
995
|
+
const res = await this.boardFetch(
|
|
996
|
+
`/api/holomesh/team/${teamId}/board/${encodeURIComponent(taskId)}`,
|
|
997
|
+
'PATCH',
|
|
998
|
+
{
|
|
999
|
+
action: 'delegate',
|
|
1000
|
+
targetTeamId: otherTeamId,
|
|
1001
|
+
}
|
|
1002
|
+
);
|
|
1003
|
+
|
|
889
1004
|
if (res?.error) throw new Error(String(res.error));
|
|
890
1005
|
return true;
|
|
891
1006
|
}
|
|
@@ -945,7 +1060,8 @@ export class Team {
|
|
|
945
1060
|
|
|
946
1061
|
/** Get presence/slot info for the team. Requires remote board. */
|
|
947
1062
|
async presence(): Promise<PresenceResult> {
|
|
948
|
-
if (!this.isRemote)
|
|
1063
|
+
if (!this.isRemote)
|
|
1064
|
+
throw new Error('presence() requires a remote board (boardUrl + boardApiKey)');
|
|
949
1065
|
const teamId = encodeURIComponent(this.name);
|
|
950
1066
|
const res = await this.boardFetch(`/api/holomesh/team/${teamId}/slots`, 'GET');
|
|
951
1067
|
if (!res) throw new Error('Failed to get presence — no response from board');
|
|
@@ -955,7 +1071,8 @@ export class Team {
|
|
|
955
1071
|
|
|
956
1072
|
/** Send a heartbeat to the team's presence system. Requires remote board. */
|
|
957
1073
|
async heartbeat(ideType?: string): Promise<HeartbeatResult> {
|
|
958
|
-
if (!this.isRemote)
|
|
1074
|
+
if (!this.isRemote)
|
|
1075
|
+
throw new Error('heartbeat() requires a remote board (boardUrl + boardApiKey)');
|
|
959
1076
|
const teamId = encodeURIComponent(this.name);
|
|
960
1077
|
const res = await this.boardFetch(`/api/holomesh/team/${teamId}/presence`, 'POST', {
|
|
961
1078
|
ide_type: ideType ?? 'unknown',
|
|
@@ -1021,7 +1138,11 @@ export class Team {
|
|
|
1021
1138
|
/** On-demand scout: parse TODO/FIXME content into tasks. */
|
|
1022
1139
|
async scoutFromTodos(grepOutput: string): Promise<TaskDef[]> {
|
|
1023
1140
|
if (this.isRemote) {
|
|
1024
|
-
const res = await this.boardFetch(
|
|
1141
|
+
const res = await this.boardFetch(
|
|
1142
|
+
`/api/holomesh/team/${encodeURIComponent(this.name)}/board/scout`,
|
|
1143
|
+
'POST',
|
|
1144
|
+
{ todo_content: grepOutput }
|
|
1145
|
+
);
|
|
1025
1146
|
if (res && res.error) throw new Error(String(res.error));
|
|
1026
1147
|
return (res?.tasks || res?.added || []) as TaskDef[];
|
|
1027
1148
|
}
|
|
@@ -1029,14 +1150,20 @@ export class Team {
|
|
|
1029
1150
|
const tasks: Array<Omit<TaskDef, 'id' | 'status' | 'createdAt'>> = [];
|
|
1030
1151
|
|
|
1031
1152
|
for (const line of grepOutput.split('\n')) {
|
|
1032
|
-
const match = line
|
|
1153
|
+
const match = line
|
|
1154
|
+
.trim()
|
|
1155
|
+
.match(/^(.+?):(\d+):\s*(?:\/\/\s*)?(TODO|FIXME|HACK|XXX)\s*:?\s*(.+)$/i);
|
|
1033
1156
|
if (!match) continue;
|
|
1034
1157
|
|
|
1035
1158
|
const [, file, lineNo, kind, detail] = match;
|
|
1036
1159
|
const upper = `${kind} ${detail}`.toUpperCase();
|
|
1037
|
-
const priority = /SECURITY|VULN|AUTH|CRITICAL/.test(upper)
|
|
1038
|
-
|
|
1039
|
-
: /
|
|
1160
|
+
const priority = /SECURITY|VULN|AUTH|CRITICAL/.test(upper)
|
|
1161
|
+
? 1
|
|
1162
|
+
: /FIXME|BUG|BROKEN|ERROR/.test(upper)
|
|
1163
|
+
? 2
|
|
1164
|
+
: /TODO|HACK|REFACTOR/.test(upper)
|
|
1165
|
+
? 3
|
|
1166
|
+
: 4;
|
|
1040
1167
|
|
|
1041
1168
|
tasks.push({
|
|
1042
1169
|
title: `${kind.toUpperCase()}: ${detail.trim().slice(0, 180)}`,
|
|
@@ -1090,7 +1217,7 @@ export class Team {
|
|
|
1090
1217
|
}
|
|
1091
1218
|
|
|
1092
1219
|
// Remove the original complex task and add sub-tasks
|
|
1093
|
-
this.board = this.board.filter(t => t.id !== task.id);
|
|
1220
|
+
this.board = this.board.filter((t) => t.id !== task.id);
|
|
1094
1221
|
await this.addTasks(subTasks);
|
|
1095
1222
|
|
|
1096
1223
|
return result;
|
|
@@ -1105,31 +1232,50 @@ export class Team {
|
|
|
1105
1232
|
|
|
1106
1233
|
async listBoard(): Promise<Record<string, unknown>> {
|
|
1107
1234
|
if (!this.isRemote) throw new Error('listBoard() requires a remote board');
|
|
1108
|
-
const res = await this.boardFetch(
|
|
1235
|
+
const res = await this.boardFetch(
|
|
1236
|
+
`/api/holomesh/team/${encodeURIComponent(this.name)}/board`,
|
|
1237
|
+
'GET'
|
|
1238
|
+
);
|
|
1109
1239
|
if (!res) throw new Error('Failed to list board');
|
|
1110
1240
|
return res as Record<string, unknown>;
|
|
1111
1241
|
}
|
|
1112
1242
|
|
|
1113
1243
|
async claimTask(taskId: string): Promise<Record<string, unknown>> {
|
|
1114
1244
|
if (!this.isRemote) throw new Error('claimTask() requires a remote board');
|
|
1115
|
-
const res = await this.boardFetch(
|
|
1245
|
+
const res = await this.boardFetch(
|
|
1246
|
+
`/api/holomesh/team/${encodeURIComponent(this.name)}/board/${encodeURIComponent(taskId)}`,
|
|
1247
|
+
'PATCH',
|
|
1248
|
+
{ action: 'claim' }
|
|
1249
|
+
);
|
|
1116
1250
|
if (!res) throw new Error('Failed to claim task');
|
|
1117
1251
|
return res as Record<string, unknown>;
|
|
1118
1252
|
}
|
|
1119
1253
|
|
|
1120
|
-
async completeTask(
|
|
1254
|
+
async completeTask(
|
|
1255
|
+
taskId: string,
|
|
1256
|
+
commit?: string,
|
|
1257
|
+
summary?: string
|
|
1258
|
+
): Promise<Record<string, unknown>> {
|
|
1121
1259
|
if (!this.isRemote) throw new Error('completeTask() requires a remote board');
|
|
1122
1260
|
const body: Record<string, unknown> = { action: 'done' };
|
|
1123
1261
|
if (commit) body.commit = commit;
|
|
1124
1262
|
if (summary) body.summary = summary;
|
|
1125
|
-
const res = await this.boardFetch(
|
|
1263
|
+
const res = await this.boardFetch(
|
|
1264
|
+
`/api/holomesh/team/${encodeURIComponent(this.name)}/board/${encodeURIComponent(taskId)}`,
|
|
1265
|
+
'PATCH',
|
|
1266
|
+
body
|
|
1267
|
+
);
|
|
1126
1268
|
if (!res) throw new Error('Failed to complete task');
|
|
1127
1269
|
return res as Record<string, unknown>;
|
|
1128
1270
|
}
|
|
1129
1271
|
|
|
1130
1272
|
async assignSlots(roles: string[]): Promise<Record<string, unknown>> {
|
|
1131
1273
|
if (!this.isRemote) throw new Error('assignSlots() requires a remote board');
|
|
1132
|
-
const res = await this.boardFetch(
|
|
1274
|
+
const res = await this.boardFetch(
|
|
1275
|
+
`/api/holomesh/team/${encodeURIComponent(this.name)}/roles`,
|
|
1276
|
+
'PATCH',
|
|
1277
|
+
{ roles }
|
|
1278
|
+
);
|
|
1133
1279
|
if (!res) throw new Error('Failed to assign slots');
|
|
1134
1280
|
return res as Record<string, unknown>;
|
|
1135
1281
|
}
|
|
@@ -1139,7 +1285,7 @@ export class Team {
|
|
|
1139
1285
|
/** Create a bounty for a board task. */
|
|
1140
1286
|
createBounty(taskId: string, reward: BountyReward, createdBy: string, deadline?: number): Bounty {
|
|
1141
1287
|
// Verify the task exists on the board
|
|
1142
|
-
const task = this.board.find(t => t.id === taskId);
|
|
1288
|
+
const task = this.board.find((t) => t.id === taskId);
|
|
1143
1289
|
if (!task) throw new Error(`Task ${taskId} not found on board`);
|
|
1144
1290
|
return this.bounties.createBounty(taskId, reward, createdBy, deadline);
|
|
1145
1291
|
}
|
|
@@ -1170,7 +1316,10 @@ export class Team {
|
|
|
1170
1316
|
title: goal.description,
|
|
1171
1317
|
description: `Autonomously synthesized goal [${goal.category}] — priority ${goal.priority}`,
|
|
1172
1318
|
priority: goal.priority === 'high' ? 2 : goal.priority === 'medium' ? 4 : 6,
|
|
1173
|
-
role:
|
|
1319
|
+
role:
|
|
1320
|
+
agentRole === 'architect' || agentRole === 'researcher'
|
|
1321
|
+
? ('researcher' as const)
|
|
1322
|
+
: ('coder' as const),
|
|
1174
1323
|
source: `synthesizer:${goal.source}`,
|
|
1175
1324
|
status: 'open',
|
|
1176
1325
|
createdAt: goal.generatedAt,
|
|
@@ -1179,11 +1328,15 @@ export class Team {
|
|
|
1179
1328
|
return task;
|
|
1180
1329
|
}
|
|
1181
1330
|
|
|
1182
|
-
private findClaimableTask(
|
|
1331
|
+
private findClaimableTask(
|
|
1332
|
+
runtime: AgentRuntime,
|
|
1333
|
+
alreadyClaimed: Set<string>,
|
|
1334
|
+
pool?: TaskDef[]
|
|
1335
|
+
): TaskDef | undefined {
|
|
1183
1336
|
const tasks = pool ?? this.openTasks;
|
|
1184
1337
|
const agent = runtime.config;
|
|
1185
1338
|
|
|
1186
|
-
const validTasks = tasks.filter(task => {
|
|
1339
|
+
const validTasks = tasks.filter((task) => {
|
|
1187
1340
|
if (alreadyClaimed.has(task.id)) return false;
|
|
1188
1341
|
if (task.priority > agent.claimFilter.maxPriority) return false;
|
|
1189
1342
|
if (task.role) return agent.claimFilter.roles.includes(task.role);
|
|
@@ -1193,23 +1346,25 @@ export class Team {
|
|
|
1193
1346
|
if (validTasks.length === 0) return undefined;
|
|
1194
1347
|
|
|
1195
1348
|
// Score and pick highest
|
|
1196
|
-
return validTasks
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1349
|
+
return validTasks
|
|
1350
|
+
.map((task) => {
|
|
1351
|
+
let score = (10 - task.priority) * 10; // Prioritize higher priority
|
|
1352
|
+
score += runtime.reputationScore;
|
|
1353
|
+
|
|
1354
|
+
const fullText = `${task.title} ${task.description}`.toLowerCase();
|
|
1355
|
+
for (const cap of agent.capabilities || []) {
|
|
1356
|
+
if (fullText.includes(cap.toLowerCase())) {
|
|
1357
|
+
score += 15; // Capability match bonus
|
|
1358
|
+
}
|
|
1204
1359
|
}
|
|
1205
|
-
}
|
|
1206
1360
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1361
|
+
if (task.role && agent.role === task.role) {
|
|
1362
|
+
score += 20; // Exact role match bonus
|
|
1363
|
+
}
|
|
1210
1364
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1365
|
+
return { task, score };
|
|
1366
|
+
})
|
|
1367
|
+
.sort((a, b) => b.score - a.score)[0]?.task;
|
|
1213
1368
|
}
|
|
1214
1369
|
|
|
1215
1370
|
private async executeTask(
|
|
@@ -1217,16 +1372,16 @@ export class Team {
|
|
|
1217
1372
|
task: TaskDef
|
|
1218
1373
|
): Promise<{ summary: string; insights: KnowledgeInsight[] }> {
|
|
1219
1374
|
const relevantKnowledge = this.knowledge.search(task.title, 3);
|
|
1220
|
-
const knowledgeContext =
|
|
1221
|
-
|
|
1222
|
-
|
|
1375
|
+
const knowledgeContext =
|
|
1376
|
+
relevantKnowledge.length > 0
|
|
1377
|
+
? relevantKnowledge.map((k) => `[${k.type}] ${k.content}`).join('\n')
|
|
1378
|
+
: '';
|
|
1223
1379
|
|
|
1224
1380
|
// Run the full 7-phase protocol cycle
|
|
1225
1381
|
const result = await runProtocolCycle(runtime.config, task, knowledgeContext);
|
|
1226
1382
|
return { summary: result.summary, insights: result.insights };
|
|
1227
1383
|
}
|
|
1228
1384
|
|
|
1229
|
-
|
|
1230
1385
|
private async getAgentVote<T>(runtime: AgentRuntime, key: string, value: T): Promise<boolean> {
|
|
1231
1386
|
const messages: LLMMessage[] = [
|
|
1232
1387
|
{
|