vgxness 0.1.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 (121) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +110 -0
  3. package/dist/agents/agent-activation-service.js +144 -0
  4. package/dist/agents/agent-registry-service.js +46 -0
  5. package/dist/agents/agent-resolver.js +249 -0
  6. package/dist/agents/agent-seed-service.js +146 -0
  7. package/dist/agents/manager-profile-overlay-service.js +34 -0
  8. package/dist/agents/profile-model-routing.js +26 -0
  9. package/dist/agents/renderers/claude-renderer.js +98 -0
  10. package/dist/agents/renderers/index.js +16 -0
  11. package/dist/agents/renderers/json-renderer.js +87 -0
  12. package/dist/agents/renderers/opencode-renderer.js +100 -0
  13. package/dist/agents/renderers/provider-adapter.js +6 -0
  14. package/dist/agents/repositories/agents.js +185 -0
  15. package/dist/agents/repositories/manager-profile-overlays.js +81 -0
  16. package/dist/agents/schema.js +1 -0
  17. package/dist/cli/dashboard-operational-read-models.js +153 -0
  18. package/dist/cli/dashboard-renderer.js +109 -0
  19. package/dist/cli/dashboard-screen-renderers.js +332 -0
  20. package/dist/cli/dashboard-tui-read-model.js +71 -0
  21. package/dist/cli/dashboard-tui-state.js +218 -0
  22. package/dist/cli/dispatcher.js +2880 -0
  23. package/dist/cli/index.js +27 -0
  24. package/dist/cli/interactive-dashboard.js +29 -0
  25. package/dist/cli/mcp-start-path.js +21 -0
  26. package/dist/cli/setup-status-renderer.js +29 -0
  27. package/dist/cli/setup-wizard-read-model.js +56 -0
  28. package/dist/cli/setup-wizard-renderer.js +148 -0
  29. package/dist/cli/setup-wizard-state.js +82 -0
  30. package/dist/cli/tui-render-helpers.js +192 -0
  31. package/dist/export/redaction.js +71 -0
  32. package/dist/harness/tools/agents.js +245 -0
  33. package/dist/harness/tools/memory.js +29 -0
  34. package/dist/mcp/client-install-opencode-contract.js +227 -0
  35. package/dist/mcp/client-install-opencode.js +194 -0
  36. package/dist/mcp/client-setup-preview.js +38 -0
  37. package/dist/mcp/control-plane.js +175 -0
  38. package/dist/mcp/doctor.js +193 -0
  39. package/dist/mcp/index.js +10 -0
  40. package/dist/mcp/opencode-default-agent-config.js +156 -0
  41. package/dist/mcp/opencode-visibility.js +102 -0
  42. package/dist/mcp/schema.js +234 -0
  43. package/dist/mcp/stdio-server.js +56 -0
  44. package/dist/mcp/validation.js +761 -0
  45. package/dist/memory/import/dry-run-planner.js +58 -0
  46. package/dist/memory/import/index.js +3 -0
  47. package/dist/memory/import/observation-writer.js +220 -0
  48. package/dist/memory/import/package.js +178 -0
  49. package/dist/memory/memory-service.js +126 -0
  50. package/dist/memory/repositories/artifacts.js +41 -0
  51. package/dist/memory/repositories/observations.js +133 -0
  52. package/dist/memory/repositories/sessions.js +105 -0
  53. package/dist/memory/repositories/traces.js +58 -0
  54. package/dist/memory/schema.js +1 -0
  55. package/dist/memory/search.js +11 -0
  56. package/dist/memory/sqlite/database.js +97 -0
  57. package/dist/memory/sqlite/migrations/001_initial.sql +128 -0
  58. package/dist/memory/sqlite/migrations/002_observation_revisions.sql +14 -0
  59. package/dist/memory/sqlite/migrations/003_agent_registry.sql +26 -0
  60. package/dist/memory/sqlite/migrations/004_run_runtime.sql +62 -0
  61. package/dist/memory/sqlite/migrations/005_run_approvals.sql +20 -0
  62. package/dist/memory/sqlite/migrations/006_run_operation_attempts.sql +32 -0
  63. package/dist/memory/sqlite/migrations/007_abandoned_operation_attempts.sql +46 -0
  64. package/dist/memory/sqlite/migrations/008_run_execution_plan_events.sql +105 -0
  65. package/dist/memory/sqlite/migrations/009_multiple_operation_attempts.sql +73 -0
  66. package/dist/memory/sqlite/migrations/010_skill_registry.sql +66 -0
  67. package/dist/memory/sqlite/migrations/011_skill_usage_resolution_outcomes.sql +21 -0
  68. package/dist/memory/sqlite/migrations/012_skill_improvement_proposals.sql +37 -0
  69. package/dist/memory/sqlite/migrations/013_skill_evaluation_scenarios.sql +43 -0
  70. package/dist/memory/sqlite/migrations/014_manager_profile_overlays.sql +14 -0
  71. package/dist/memory/storage-paths.js +72 -0
  72. package/dist/orchestrator/natural-language-planner.js +191 -0
  73. package/dist/orchestrator/schema.js +1 -0
  74. package/dist/permissions/index.js +2 -0
  75. package/dist/permissions/policy-evaluator.js +109 -0
  76. package/dist/permissions/schema.js +1 -0
  77. package/dist/providers/opencode/injection-preview.js +134 -0
  78. package/dist/providers/opencode/manager-payload.js +129 -0
  79. package/dist/runs/execution-planning.js +117 -0
  80. package/dist/runs/operation-execution.js +1 -0
  81. package/dist/runs/operation-retry.js +124 -0
  82. package/dist/runs/repositories/runs.js +611 -0
  83. package/dist/runs/run-insights.js +145 -0
  84. package/dist/runs/run-service.js +713 -0
  85. package/dist/runs/run-snapshot-export-service.js +31 -0
  86. package/dist/runs/sandbox-process-execution.js +218 -0
  87. package/dist/runs/sandbox-worktree-planning.js +59 -0
  88. package/dist/runs/schema.js +1 -0
  89. package/dist/sdd/artifact-portability-service.js +118 -0
  90. package/dist/sdd/schema.js +17 -0
  91. package/dist/sdd/sdd-workflow-service.js +217 -0
  92. package/dist/setup/backup-rollback-service.js +76 -0
  93. package/dist/setup/index.js +3 -0
  94. package/dist/setup/providers/antigravity-setup-adapter.js +18 -0
  95. package/dist/setup/providers/claude-setup-adapter.js +30 -0
  96. package/dist/setup/providers/custom-setup-adapter.js +18 -0
  97. package/dist/setup/providers/index.js +6 -0
  98. package/dist/setup/providers/opencode-setup-adapter.js +104 -0
  99. package/dist/setup/providers/provider-setup-adapter.js +15 -0
  100. package/dist/setup/providers/provider-setup-registry.js +11 -0
  101. package/dist/setup/schema.js +1 -0
  102. package/dist/setup/setup-defaults.js +11 -0
  103. package/dist/setup/setup-lifecycle-service.js +175 -0
  104. package/dist/setup/setup-plan.js +105 -0
  105. package/dist/skills/repositories/skill-evaluation-scenarios.js +289 -0
  106. package/dist/skills/repositories/skill-improvement-proposals.js +288 -0
  107. package/dist/skills/repositories/skills.js +430 -0
  108. package/dist/skills/schema.js +1 -0
  109. package/dist/skills/skill-payload.js +94 -0
  110. package/dist/skills/skill-registry-service.js +92 -0
  111. package/dist/skills/skill-resolver.js +191 -0
  112. package/dist/workflows/command-allowlist-adapter.js +70 -0
  113. package/dist/workflows/schema.js +4 -0
  114. package/dist/workflows/workflow-executor.js +345 -0
  115. package/dist/workflows/workflow-registry.js +66 -0
  116. package/docs/architecture.md +698 -0
  117. package/docs/cli.md +741 -0
  118. package/docs/funcionamiento-del-sistema.md +868 -0
  119. package/docs/harness-gap-analysis.md +229 -0
  120. package/docs/prd.md +372 -0
  121. package/package.json +57 -0
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Proprietary License
2
+
3
+ Copyright (c) 2026 VGXNESS. All rights reserved.
4
+
5
+ This package is proprietary software. You may install and use it only as authorized by the copyright holder. No rights are granted to copy, modify, redistribute, sublicense, sell, publish, or make derivative works from this software except under a separate written agreement from the copyright holder.
6
+
7
+ The npm package contains inspectable JavaScript runtime artifacts for installation and execution. That technical inspectability does not grant an open-source license or redistribution rights.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # VGXNESS
2
+
3
+ VGXNESS is an alpha CLI and MCP control plane for guided AI-agent workflows, SDD artifacts, local memory, and OpenCode integration.
4
+
5
+ ## Alpha status and license
6
+
7
+ This package is proprietary software. The npm package ships inspectable JavaScript (`dist/`) so Node can run it, but it is **not open-source licensed** and may not be redistributed unless you have written permission. See [LICENSE](./LICENSE).
8
+
9
+ OpenCode is the primary supported provider in this alpha. Other providers are preview/manual only. The dashboard is read-only/copy-only; provider config writes require explicit CLI confirmation.
10
+
11
+ ## Requirements
12
+
13
+ - Node.js >= 22
14
+ - npm
15
+ - A platform supported by `better-sqlite3` for your Node/runtime combination
16
+
17
+ ## Install globally
18
+
19
+ ```bash
20
+ npm install -g vgxness
21
+ vgx --help
22
+ vgxness --help
23
+ ```
24
+
25
+ Use the `alpha` npm dist-tag for alpha releases when publishing/consuming pre-release builds.
26
+
27
+ ## First setup with OpenCode
28
+
29
+ Run the guided setup wizard in a TTY, or preview the same safe setup plan in non-interactive shells:
30
+
31
+ ```bash
32
+ vgx init
33
+ # equivalent explicit command
34
+ vgx setup plan
35
+ ```
36
+
37
+ Defaults for the alpha are: package `vgxness`, provider `opencode`, global user data DB, project OpenCode scope, and `mcp-plus-agents` mode. The generated MCP command for the global DB default is:
38
+
39
+ ```bash
40
+ vgxness mcp start
41
+ ```
42
+
43
+ Apply only after reviewing the plan:
44
+
45
+ ```bash
46
+ vgx setup apply --yes
47
+ ```
48
+
49
+ For project-local or custom databases, use `--db project-local` or `--db custom --db-path <path>` with setup commands. Existing low-level commands remain available:
50
+
51
+ ```bash
52
+ vgxness mcp install opencode --plan
53
+ vgxness mcp install opencode --yes
54
+ ```
55
+
56
+ ## Verify
57
+
58
+ ```bash
59
+ vgx doctor
60
+ vgxness mcp doctor opencode
61
+ vgxness setup status
62
+ ```
63
+
64
+ Restart OpenCode after applying config and verify that the `vgxness` MCP server is visible.
65
+
66
+ ## Rollback and recovery
67
+
68
+ OpenCode applies create a backup before merging existing config. Restore one with:
69
+
70
+ ```bash
71
+ vgx setup rollback --backup <path>
72
+ ```
73
+
74
+ Rollback validates the backup, creates a pre-rollback backup of the current target when present, restores the selected backup byte-for-byte, and keeps the original backup if anything fails. Rerun `vgx doctor` after rollback.
75
+
76
+ Common issues:
77
+
78
+ - Node < 22: upgrade Node and reinstall.
79
+ - Native SQLite install failure: verify your platform has compatible `better-sqlite3` binaries/build tools.
80
+ - JSONC or malformed OpenCode config: convert to valid JSON or resolve manually; VGXNESS refuses unsafe rewrites.
81
+ - Existing `mcp.vgxness`: inspect the existing entry before rerunning setup; VGXNESS refuses overwrite by default.
82
+
83
+ ## Release verification matrix
84
+
85
+ CI and manual release checks target Node 22 on macOS, Linux, and Windows:
86
+
87
+ ```bash
88
+ npm ci
89
+ npm run typecheck
90
+ npm test
91
+ npm run package:release-check
92
+ npm run package:dry-run
93
+ npm run package:smoke:install
94
+ ```
95
+
96
+ Do not publish from CI. Use `npm publish --dry-run --tag alpha` as a final manual gate only.
97
+
98
+ ## Uninstall
99
+
100
+ ```bash
101
+ npm uninstall -g vgxness
102
+ ```
103
+
104
+ Remove any OpenCode config entries and local/global VGXNESS data manually if you no longer need them.
105
+
106
+ ## More docs
107
+
108
+ - [CLI reference](./docs/cli.md)
109
+ - [Architecture](./docs/architecture.md)
110
+ - [System behavior](./docs/funcionamiento-del-sistema.md)
@@ -0,0 +1,144 @@
1
+ import { resolveAgentProfileModel } from './profile-model-routing.js';
2
+ const defaultAgentName = 'vgxness-manager';
3
+ const provider = 'opencode';
4
+ const nextSafeAction = 'review_provider_payload_and_choose_manual_handoff';
5
+ const safety = { executesProvider: false, writesProviderConfig: false, backgroundWorker: false, autonomy: false };
6
+ export class AgentActivationService {
7
+ dependencies;
8
+ constructor(dependencies) {
9
+ this.dependencies = dependencies;
10
+ }
11
+ activate(input) {
12
+ const validated = validateActivationInput(input);
13
+ if (!validated.ok)
14
+ return validated;
15
+ const agent = this.resolveAgent(validated.value);
16
+ if (!agent.ok)
17
+ return agent;
18
+ if (agent.value.mode !== 'agent')
19
+ return validationFailure('Agent activation requires a top-level agent');
20
+ const selection = resolveAgentProfileModel({ agent: agent.value, ...(validated.value.providerAdapter === undefined ? {} : { providerAdapter: validated.value.providerAdapter }) });
21
+ if (!selection.ok)
22
+ return selection;
23
+ if (selection.value.providerAdapter !== provider)
24
+ return validationFailure(`Provider adapter is not supported for activation: ${selection.value.providerAdapter}`);
25
+ const run = this.dependencies.runs.createRun({
26
+ project: validated.value.project,
27
+ userIntent: validated.value.userIntent,
28
+ workflow: validated.value.workflow,
29
+ phase: validated.value.phase,
30
+ selectedAgentId: agent.value.id,
31
+ providerAdapter: selection.value.providerAdapter,
32
+ model: selection.value.model,
33
+ });
34
+ if (!run.ok)
35
+ return run;
36
+ const payload = this.dependencies.opencodeManagerPayload.build(toPayloadInput(validated.value, agent.value.id));
37
+ if (!payload.ok)
38
+ return this.failAfterRun(run.value.id, payload.error.code, payload.error.message);
39
+ const envelopeWithoutRun = {
40
+ version: 1,
41
+ agent: { id: agent.value.id, name: agent.value.name, scope: agent.value.scope, mode: 'agent' },
42
+ provider,
43
+ modelSelection: selection.value,
44
+ providerPayload: payload.value,
45
+ ...(payload.value.skillPayload !== undefined ? { skillsContext: payload.value.skillPayload } : {}),
46
+ nextSafeAction,
47
+ safety,
48
+ warnings: [
49
+ 'Agent activation prepared a provider-ready payload but does not execute providers.',
50
+ 'No provider configuration, background worker, autonomous step, or secrets expansion was performed.',
51
+ ...selection.value.warnings,
52
+ ...payload.value.warnings,
53
+ ],
54
+ };
55
+ const checkpoint = this.dependencies.runs.appendCheckpoint({
56
+ runId: run.value.id,
57
+ label: 'agent-activation-initial',
58
+ state: toJson({ ...envelopeWithoutRun, run: { id: run.value.id, status: 'created', checkpointId: null } }),
59
+ });
60
+ if (!checkpoint.ok)
61
+ return this.failAfterRun(run.value.id, checkpoint.error.code, checkpoint.error.message);
62
+ const event = this.dependencies.runs.appendEvent({
63
+ runId: run.value.id,
64
+ kind: 'timeline',
65
+ title: 'Agent activation prepared',
66
+ payload: toJson({
67
+ agent: envelopeWithoutRun.agent,
68
+ provider,
69
+ modelSelection: selection.value,
70
+ nextSafeAction,
71
+ safety,
72
+ checkpointId: checkpoint.value.id,
73
+ }),
74
+ relatedType: 'run-checkpoint',
75
+ relatedId: checkpoint.value.id,
76
+ });
77
+ if (!event.ok)
78
+ return this.failAfterRun(run.value.id, event.error.code, event.error.message);
79
+ return ok({ ...envelopeWithoutRun, run: { id: run.value.id, status: 'created', checkpointId: checkpoint.value.id } });
80
+ }
81
+ resolveAgent(input) {
82
+ if (input.agentId !== undefined)
83
+ return this.dependencies.agents.getAgent(input.agentId);
84
+ if (this.dependencies.managerProfiles !== undefined) {
85
+ const effective = this.dependencies.managerProfiles.resolveEffectiveManager({ project: input.project, scope: input.scope, managerName: defaultAgentName });
86
+ return effective.ok ? ok(effective.value.manager) : effective;
87
+ }
88
+ return this.dependencies.agents.getAgentByName(input.project, input.scope, defaultAgentName);
89
+ }
90
+ failAfterRun(runId, code, message) {
91
+ this.dependencies.runs.appendEvent({
92
+ runId,
93
+ kind: 'timeline',
94
+ title: 'Agent activation failed',
95
+ payload: { error: { code: String(code), message }, nextSafeActionReturned: false },
96
+ });
97
+ this.dependencies.runs.updateFinalStatus({ runId, status: 'failed', outcome: 'failure', outcomeReason: message });
98
+ return { ok: false, error: { code, message } };
99
+ }
100
+ }
101
+ function validateActivationInput(input) {
102
+ if (input.project.trim().length === 0)
103
+ return validationFailure('Project is required');
104
+ if (input.userIntent.trim().length === 0)
105
+ return validationFailure('userIntent is required');
106
+ if (input.agentId !== undefined && input.agentId.trim().length === 0)
107
+ return validationFailure('agentId must not be empty');
108
+ const validated = {
109
+ project: input.project.trim(),
110
+ scope: input.scope ?? 'project',
111
+ userIntent: input.userIntent.trim(),
112
+ workflow: input.workflow?.trim() || 'agent-activation',
113
+ phase: input.phase?.trim() || 'activation',
114
+ };
115
+ if (input.agentId !== undefined)
116
+ validated.agentId = input.agentId.trim();
117
+ if (input.workspaceRoot !== undefined)
118
+ validated.workspaceRoot = input.workspaceRoot;
119
+ if (input.maxSourceBytes !== undefined)
120
+ validated.maxSourceBytes = input.maxSourceBytes;
121
+ if (input.providerAdapter !== undefined) {
122
+ if (input.providerAdapter.trim().length === 0)
123
+ return validationFailure('providerAdapter must not be empty');
124
+ validated.providerAdapter = input.providerAdapter.trim();
125
+ }
126
+ return ok(validated);
127
+ }
128
+ function toPayloadInput(input, agentId) {
129
+ const payload = { project: input.project, scope: input.scope };
130
+ if (input.agentId !== undefined)
131
+ payload.agentId = agentId;
132
+ else
133
+ payload.useManagerOverlay = true;
134
+ if (input.workspaceRoot !== undefined)
135
+ payload.workspaceRoot = input.workspaceRoot;
136
+ if (input.maxSourceBytes !== undefined)
137
+ payload.maxSourceBytes = input.maxSourceBytes;
138
+ return payload;
139
+ }
140
+ function toJson(value) {
141
+ return JSON.parse(JSON.stringify(value));
142
+ }
143
+ function ok(value) { return { ok: true, value }; }
144
+ function validationFailure(message) { return { ok: false, error: { code: 'validation_failed', message } }; }
@@ -0,0 +1,46 @@
1
+ import { AgentResolver } from './agent-resolver.js';
2
+ import { AgentRepository } from './repositories/agents.js';
3
+ export class AgentRegistryService {
4
+ database;
5
+ agents;
6
+ constructor(database) {
7
+ this.database = database;
8
+ this.agents = new AgentRepository(database);
9
+ }
10
+ registerAgent(input) {
11
+ return this.agents.register({ ...input, mode: 'agent' });
12
+ }
13
+ registerSubagent(input) {
14
+ return this.agents.register({ ...input, mode: 'subagent' });
15
+ }
16
+ getAgent(id) {
17
+ return this.agents.getById(id);
18
+ }
19
+ getAgentByName(project, scope, name) {
20
+ return this.agents.getByName(project, scope, name);
21
+ }
22
+ listAgents(filters = {}) {
23
+ return this.agents.list(filters);
24
+ }
25
+ listSubagents(parentAgentId) {
26
+ return this.agents.list({ mode: 'subagent', parentAgentId });
27
+ }
28
+ resolveAgents(input) {
29
+ return new AgentResolver(() => this.listAgentDefinitions(input)).resolve(input);
30
+ }
31
+ close() { this.database.close(); }
32
+ listAgentDefinitions(input) {
33
+ void input;
34
+ const summaries = this.agents.list();
35
+ if (!summaries.ok)
36
+ return summaries;
37
+ const definitions = [];
38
+ for (const summary of summaries.value) {
39
+ const agent = this.agents.getById(summary.id);
40
+ if (!agent.ok)
41
+ return agent;
42
+ definitions.push(agent.value);
43
+ }
44
+ return { ok: true, value: definitions };
45
+ }
46
+ }
@@ -0,0 +1,249 @@
1
+ export class AgentResolver {
2
+ loadAgents;
3
+ constructor(loadAgents) {
4
+ this.loadAgents = loadAgents;
5
+ }
6
+ resolve(input) {
7
+ const agents = this.loadAgents();
8
+ if (!agents.ok)
9
+ return agents;
10
+ const normalized = normalizeInput(input);
11
+ const candidates = [];
12
+ const skipped = [];
13
+ for (const agent of agents.value) {
14
+ const skipReasons = hardSkipReasons(agent, normalized);
15
+ if (skipReasons.length > 0) {
16
+ skipped.push(toSkipped(agent, skipReasons));
17
+ continue;
18
+ }
19
+ const scored = scoreAgent(agent, normalized);
20
+ if (scored.score <= 0) {
21
+ skipped.push(toSkipped(agent, ['no matching resolution signals']));
22
+ continue;
23
+ }
24
+ candidates.push(scored);
25
+ }
26
+ candidates.sort(compareCandidates);
27
+ skipped.sort(compareSkipped);
28
+ return ok({
29
+ context: {
30
+ ...(normalized.project !== undefined ? { project: normalized.project } : {}),
31
+ ...(normalized.scope !== undefined ? { scope: normalized.scope } : {}),
32
+ ...(normalized.taskDescription !== undefined ? { taskDescription: normalized.taskDescription } : {}),
33
+ ...(normalized.intent !== undefined ? { intent: normalized.intent } : {}),
34
+ desiredCapabilities: normalized.desiredCapabilities,
35
+ ...(normalized.workflow !== undefined ? { workflow: normalized.workflow } : {}),
36
+ ...(normalized.phase !== undefined ? { phase: normalized.phase } : {}),
37
+ ...(normalized.providerAdapter !== undefined ? { providerAdapter: normalized.providerAdapter } : {}),
38
+ ...(normalized.mode !== undefined ? { mode: normalized.mode } : {}),
39
+ },
40
+ candidates,
41
+ skipped,
42
+ });
43
+ }
44
+ }
45
+ const SDD_PHASE_SEMANTICS = [
46
+ {
47
+ name: 'sdd-planning',
48
+ phases: ['proposal', 'design', 'tasks'],
49
+ terms: ['plan', 'planning', 'proposal', 'propose', 'design', 'task', 'tasks', 'breakdown', 'slice', 'slicing'],
50
+ },
51
+ {
52
+ name: 'sdd-verification',
53
+ phases: ['verify'],
54
+ terms: ['verify', 'verification', 'test', 'testing', 'review', 'risk', 'evidence'],
55
+ },
56
+ ];
57
+ function normalizeInput(input) {
58
+ const desiredCapabilities = normalizeList(input.desiredCapabilities ?? []);
59
+ const taskDescription = normalizeText(input.taskDescription);
60
+ const intent = normalizeText(input.intent);
61
+ return {
62
+ ...input,
63
+ ...(taskDescription !== undefined ? { taskDescription } : {}),
64
+ ...(intent !== undefined ? { intent } : {}),
65
+ desiredCapabilities,
66
+ terms: tokenize([taskDescription, intent].filter((value) => value !== undefined).join(' ')),
67
+ };
68
+ }
69
+ function hardSkipReasons(agent, input) {
70
+ const reasons = [];
71
+ if (input.project !== undefined && agent.project !== input.project)
72
+ reasons.push(`project mismatch: ${agent.project}`);
73
+ if (input.scope !== undefined && agent.scope !== input.scope)
74
+ reasons.push(`scope mismatch: ${agent.scope}`);
75
+ if (input.mode !== undefined && agent.mode !== input.mode)
76
+ reasons.push(`mode mismatch: ${agent.mode}`);
77
+ if (input.providerAdapter !== undefined && Object.keys(agent.adapters).length > 0 && agent.adapters[input.providerAdapter] === undefined)
78
+ reasons.push(`provider adapter mismatch: ${input.providerAdapter}`);
79
+ if ((input.workflow !== undefined || input.phase !== undefined) && agent.workflows.length > 0 && workflowMatches(agent, input).length === 0)
80
+ reasons.push('workflow mismatch');
81
+ if (input.desiredCapabilities.length > 0 && matchingValues(agent.capabilities, input.desiredCapabilities).length === 0)
82
+ reasons.push('capability mismatch');
83
+ return reasons;
84
+ }
85
+ function scoreAgent(agent, input) {
86
+ const reasons = [];
87
+ let score = 0;
88
+ const capabilityMatches = matchingValues(agent.capabilities, input.desiredCapabilities);
89
+ if (capabilityMatches.length > 0) {
90
+ score += capabilityMatches.length * 30;
91
+ reasons.push(`capabilities matched: ${capabilityMatches.join(', ')}`);
92
+ }
93
+ const workflowMatchesValue = workflowSignalMatches(agent, input);
94
+ if (workflowMatchesValue.exactPhase.length > 0) {
95
+ score += workflowMatchesValue.exactPhase.length * 25;
96
+ reasons.push(`workflow phase matched: ${workflowMatchesValue.exactPhase.join(', ')}`);
97
+ }
98
+ if (workflowMatchesValue.phaseOnly.length > 0) {
99
+ score += workflowMatchesValue.phaseOnly.length * 22;
100
+ reasons.push(`workflow phase-only matched: ${workflowMatchesValue.phaseOnly.join(', ')}`);
101
+ }
102
+ if (workflowMatchesValue.broad.length > 0) {
103
+ score += workflowMatchesValue.broad.length * 20;
104
+ reasons.push(`workflow matched: ${workflowMatchesValue.broad.join(', ')}`);
105
+ }
106
+ if (input.providerAdapter !== undefined) {
107
+ if (agent.adapters[input.providerAdapter] !== undefined) {
108
+ score += 20;
109
+ reasons.push(`provider adapter matched: ${input.providerAdapter}`);
110
+ }
111
+ else if (Object.keys(agent.adapters).length === 0) {
112
+ score += 5;
113
+ reasons.push(`provider neutral: no adapter restrictions for ${input.providerAdapter}`);
114
+ }
115
+ }
116
+ const skillMatches = matchingValues(agent.skills, [...input.desiredCapabilities, ...input.terms]);
117
+ if (skillMatches.length > 0) {
118
+ score += skillMatches.length * 10;
119
+ reasons.push(`skills matched: ${skillMatches.join(', ')}`);
120
+ }
121
+ const textMatches = textSignalMatches(agent, input.terms);
122
+ if (textMatches.length > 0) {
123
+ score += Math.min(textMatches.length, 5) * 4;
124
+ reasons.push(`task text matched: ${textMatches.join(', ')}`);
125
+ }
126
+ const phaseSemanticMatches = phaseSemanticMatchesFor(agent, input);
127
+ for (const match of phaseSemanticMatches) {
128
+ score += 8;
129
+ reasons.push(`phase semantics matched: ${match}`);
130
+ }
131
+ if (input.mode !== undefined) {
132
+ score += 3;
133
+ reasons.push(`mode matched: ${agent.mode}`);
134
+ }
135
+ if (agent.mode === 'subagent') {
136
+ score += 2;
137
+ reasons.push(`parent constraint satisfied: ${agent.parentAgentId}`);
138
+ }
139
+ return { agent, score, reasons };
140
+ }
141
+ function workflowMatches(agent, input) {
142
+ const matches = workflowSignalMatches(agent, input);
143
+ return [...matches.broad, ...matches.phaseOnly, ...matches.exactPhase];
144
+ }
145
+ function workflowSignalMatches(agent, input) {
146
+ const requested = [
147
+ input.workflow,
148
+ input.phase,
149
+ input.workflow !== undefined && input.phase !== undefined ? `${input.workflow}:${input.phase}` : undefined,
150
+ ].filter((value) => value !== undefined).map(normalizeKey);
151
+ if (requested.length === 0)
152
+ return { broad: [], phaseOnly: [], exactPhase: [] };
153
+ const workflow = input.workflow !== undefined ? normalizeKey(input.workflow) : undefined;
154
+ const phase = input.phase !== undefined ? normalizeKey(input.phase) : undefined;
155
+ const exact = workflow !== undefined && phase !== undefined ? `${workflow}:${phase}` : undefined;
156
+ const matches = { broad: [], phaseOnly: [], exactPhase: [] };
157
+ for (const agentWorkflow of agent.workflows) {
158
+ const normalizedWorkflow = normalizeKey(agentWorkflow);
159
+ if (!requested.includes(normalizedWorkflow))
160
+ continue;
161
+ if (exact !== undefined && normalizedWorkflow === exact)
162
+ matches.exactPhase.push(agentWorkflow);
163
+ else if (phase !== undefined && normalizedWorkflow === phase)
164
+ matches.phaseOnly.push(agentWorkflow);
165
+ else
166
+ matches.broad.push(agentWorkflow);
167
+ }
168
+ return matches;
169
+ }
170
+ function phaseSemanticMatchesFor(agent, input) {
171
+ const workflow = input.workflow !== undefined ? normalizeKey(input.workflow) : undefined;
172
+ if (workflow !== undefined && workflow !== 'sdd')
173
+ return [];
174
+ const inputPhase = input.phase !== undefined ? normalizeKey(input.phase) : undefined;
175
+ const agentTerms = tokenize([
176
+ agent.name,
177
+ agent.description,
178
+ ...agent.capabilities,
179
+ ...agent.skills,
180
+ ...agent.workflows,
181
+ ].join(' '));
182
+ const requestTerms = new Set(input.terms);
183
+ const matches = [];
184
+ for (const semantic of SDD_PHASE_SEMANTICS) {
185
+ const phaseMatches = inputPhase !== undefined && semantic.phases.includes(inputPhase);
186
+ const termMatches = semantic.terms.some((term) => requestTerms.has(term));
187
+ const agentMatches = semantic.terms.some((term) => agentTerms.includes(term));
188
+ if ((phaseMatches || termMatches) && agentMatches)
189
+ matches.push(semantic.name);
190
+ }
191
+ return matches;
192
+ }
193
+ function textSignalMatches(agent, terms) {
194
+ if (terms.length === 0)
195
+ return [];
196
+ const searchable = tokenize([
197
+ agent.name,
198
+ agent.description,
199
+ ...agent.capabilities,
200
+ ...agent.skills,
201
+ ...agent.workflows,
202
+ ].join(' '));
203
+ return terms.filter((term) => searchable.includes(term));
204
+ }
205
+ function matchingValues(values, requested) {
206
+ const normalizedValues = new Map(values.map((value) => [normalizeKey(value), value]));
207
+ const matches = [];
208
+ for (const requestedValue of requested) {
209
+ const match = normalizedValues.get(normalizeKey(requestedValue));
210
+ if (match !== undefined && !matches.includes(match))
211
+ matches.push(match);
212
+ }
213
+ return matches;
214
+ }
215
+ function compareCandidates(a, b) {
216
+ return b.score - a.score
217
+ || modeRank(a.agent.mode) - modeRank(b.agent.mode)
218
+ || a.agent.name.localeCompare(b.agent.name)
219
+ || a.agent.id.localeCompare(b.agent.id);
220
+ }
221
+ function compareSkipped(a, b) {
222
+ return a.agent.name.localeCompare(b.agent.name) || a.agent.id.localeCompare(b.agent.id);
223
+ }
224
+ function modeRank(mode) {
225
+ return mode === 'agent' ? 0 : 1;
226
+ }
227
+ function toSkipped(agent, reasons) {
228
+ return { agent: { id: agent.id, name: agent.name, mode: agent.mode, project: agent.project, scope: agent.scope }, reasons };
229
+ }
230
+ function normalizeList(values) {
231
+ return values.map(normalizeText).filter((value) => value !== undefined);
232
+ }
233
+ function normalizeText(value) {
234
+ const normalized = value?.trim();
235
+ return normalized ? normalized : undefined;
236
+ }
237
+ function normalizeKey(value) {
238
+ return value.trim().toLowerCase();
239
+ }
240
+ function tokenize(value) {
241
+ const seen = new Set();
242
+ return value.toLowerCase().split(/[^a-z0-9-]+/).filter(Boolean).filter((term) => {
243
+ if (term.length < 3 || seen.has(term))
244
+ return false;
245
+ seen.add(term);
246
+ return true;
247
+ });
248
+ }
249
+ function ok(value) { return { ok: true, value }; }